1 // Copyright (c) 2019 Geometry Factory
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org)
5 //
6 // $URL: https://github.com/CGAL/cgal/blob/v5.3/Stream_support/include/CGAL/IO/3MF/write_3mf.h $
7 // $Id: write_3mf.h 1f45360 2021-01-26T09:05:24+01:00 Mael Rouxel-Labbé
8 // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 // Author(s) : Maxime Gimeno
11
12 #ifndef CGAL_IO_WRITE_3MF_H
13 #define CGAL_IO_WRITE_3MF_H
14
15 #ifdef CGAL_LINKED_WITH_3MF
16
17 #include <CGAL/IO/Color.h>
18
19 #include <CGAL/boost/graph/iterator.h>
20
21 #include <Model/COM/NMR_DLLInterfaces.h>
22
23 #include <iostream>
24 #include <vector>
25 #include <string>
26
27 /*
28 * \attention Only versions inferior to 2.0 of lib3mf are supported.
29 * */
30 namespace CGAL {
31 namespace tmf_internal {
32
33 // Utility functions to create vertices and triangles
fnCreateVertex(float x,float y,float z)34 NMR::MODELMESHVERTEX fnCreateVertex(float x, float y, float z)
35 {
36 NMR::MODELMESHVERTEX result;
37 result.m_fPosition[0] = x;
38 result.m_fPosition[1] = y;
39 result.m_fPosition[2] = z;
40 return result;
41 }
42
fnCreateTriangle(int v0,int v1,int v2)43 NMR::MODELMESHTRIANGLE fnCreateTriangle(int v0, int v1, int v2)
44 {
45 NMR::MODELMESHTRIANGLE result;
46 result.m_nIndices[0] = v0;
47 result.m_nIndices[1] = v1;
48 result.m_nIndices[2] = v2;
49 return result;
50 }
51
52 NMR::MODELMESHCOLOR_SRGB fnCreateColor(unsigned char red, unsigned char green,
53 unsigned char blue, unsigned char alpha=255)
54 {
55 NMR::MODELMESHCOLOR_SRGB result;
56 result.m_Red = red;
57 result.m_Green = green;
58 result.m_Blue = blue;
59 result.m_Alpha = alpha;
60 return result;
61 }
62
63 } // namespace tmf_internal
64
65 namespace IO {
66
add_build_item(NMR::PLib3MFModel * pModel,NMR::PLib3MFModelMeshObject * pMeshObject)67 bool add_build_item(NMR::PLib3MFModel * pModel,
68 NMR::PLib3MFModelMeshObject* pMeshObject)
69 {
70 HRESULT hResult;
71 DWORD nErrorMessage;
72 LPCSTR pszErrorMessage;
73
74 // Add Build Item for Mesh
75 NMR::PLib3MFModelBuildItem * pBuildItem;
76 hResult = NMR::lib3mf_model_addbuilditem(pModel, pMeshObject, NULL, &pBuildItem);
77
78 if(hResult != LIB3MF_OK)
79 {
80 std::cerr << "could not create build item: " << std::hex << hResult << std::endl;
81 NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage);
82 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
83 NMR::lib3mf_release(pMeshObject);
84 NMR::lib3mf_release(pModel);
85 return false;
86 }
87
88 // Release BuildItem and Mesh
89 NMR::lib3mf_release(pMeshObject);
90 NMR::lib3mf_release(pBuildItem);
91 return true;
92 }
93
export_model_to_file(const std::string & file_name,NMR::PLib3MFModel * pModel)94 bool export_model_to_file(const std::string& file_name,
95 NMR::PLib3MFModel * pModel)
96 {
97 HRESULT hResult;
98 DWORD nErrorMessage;
99 LPCSTR pszErrorMessage;
100
101 // Output mesh as 3MF
102 // Create Model Writer for 3MF
103 NMR::PLib3MFModelWriter * p3MFWriter;
104 hResult = NMR::lib3mf_model_querywriter(pModel, "3mf", &p3MFWriter);
105
106 if(hResult != LIB3MF_OK)
107 {
108 std::cerr << "could not create model reader: " << std::hex << hResult << std::endl;
109 NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage);
110 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
111 NMR::lib3mf_release(pModel);
112 return false;
113 }
114
115 // Export Model into File
116 hResult = NMR::lib3mf_writer_writetofileutf8(p3MFWriter, file_name.c_str());
117 if(hResult != LIB3MF_OK) {
118 std::cerr << "could not write file: " << std::hex << hResult << std::endl;
119 NMR::lib3mf_getlasterror(p3MFWriter, &nErrorMessage, &pszErrorMessage);
120 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
121 NMR::lib3mf_release(pModel);
122 NMR::lib3mf_release(p3MFWriter);
123 return false;
124 }
125
126 // Release Model Writer
127 NMR::lib3mf_release(p3MFWriter);
128 return true;
129 }
130
131 template<typename PointRange, typename TriangleRange, typename ColorRange>
write_mesh_to_model(const PointRange & points,const TriangleRange & triangles,const ColorRange & colors,const std::string & name,NMR::PLib3MFModelMeshObject ** pMeshObject,NMR::PLib3MFModel * pModel)132 bool write_mesh_to_model(const PointRange& points,
133 const TriangleRange& triangles,
134 const ColorRange& colors,
135 const std::string& name,
136 NMR::PLib3MFModelMeshObject** pMeshObject,
137 NMR::PLib3MFModel * pModel)
138 {
139 DWORD nErrorMessage;
140 LPCSTR pszErrorMessage;
141 HRESULT hResult;
142
143 // Create mesh structure
144 std::vector<NMR::MODELMESHVERTEX> pVertices;
145 std::vector<NMR::MODELMESHTRIANGLE> pTriangles;
146
147 // Create Mesh Object
148 hResult = NMR::lib3mf_model_addmeshobject(pModel, pMeshObject);
149 if(hResult != LIB3MF_OK)
150 {
151 std::cerr << "could not add mesh object: " << std::hex << hResult << std::endl;
152 NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage);
153 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
154 NMR::lib3mf_release(pModel);
155 return false;
156 }
157
158 for(const auto& point : points)
159 pVertices.push_back(tmf_internal::fnCreateVertex(point.x(), point.y(), point.z()));
160
161 for(const auto& triangle : triangles)
162 pTriangles.push_back(tmf_internal::fnCreateTriangle(triangle[0], triangle[1], triangle[2]));
163
164 hResult = NMR::lib3mf_meshobject_setgeometry(*pMeshObject, pVertices.data(),
165 pVertices.size(), pTriangles.data(),
166 pTriangles.size());
167 if(hResult != LIB3MF_OK)
168 {
169 std::cerr << "could not set mesh geometry: " << std::hex << hResult << std::endl;
170 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
171 std::cerr << "error #" << std::hex << nErrorMessage
172 << ": " << pszErrorMessage << std::endl;
173 NMR::lib3mf_release(*pMeshObject);
174 NMR::lib3mf_release(pModel);
175 return false;
176 }
177
178 // Create color entries
179 NMR::PLib3MFPropertyHandler * pPropertyHandler;
180 hResult = NMR::lib3mf_meshobject_createpropertyhandler(*pMeshObject, &pPropertyHandler);
181 if(hResult != LIB3MF_OK)
182 {
183 std::cerr << "could not create property handler: " << std::hex << hResult << std::endl;
184 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
185 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
186 NMR::lib3mf_release(*pMeshObject);
187 NMR::lib3mf_release(pModel);
188 return false;
189 }
190
191 // define colors
192 for(std::size_t pid = 0; pid<colors.size(); ++pid)
193 {
194 NMR::MODELMESHCOLOR_SRGB sColor = tmf_internal::fnCreateColor (colors[pid].red(),
195 colors[pid].green(),
196 colors[pid].blue(),
197 colors[pid].alpha());
198 // One-colored Triangles
199 NMR::lib3mf_propertyhandler_setsinglecolor(pPropertyHandler, pid, &sColor);
200 }
201
202 // make sure to define a default property
203 NMR::PLib3MFDefaultPropertyHandler * pDefaultPropertyHandler;
204 hResult = NMR::lib3mf_object_createdefaultpropertyhandler(*pMeshObject, &pDefaultPropertyHandler);
205 if(hResult != LIB3MF_OK)
206 {
207 std::cerr<< "could not create default property handler: " << std::hex << hResult << std::endl;
208 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
209 std::cerr<< "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
210 NMR::lib3mf_release(*pMeshObject);
211 NMR::lib3mf_release(pModel);
212 return false;
213 }
214
215 NMR::MODELMESHCOLOR_SRGB default_color = tmf_internal::fnCreateColor(0,0,0,0);
216 NMR::lib3mf_defaultpropertyhandler_setcolor(pDefaultPropertyHandler, &default_color);
217
218 // release default property handler
219 NMR::lib3mf_release(pDefaultPropertyHandler);
220
221 // Set name
222 hResult = NMR::lib3mf_object_setnameutf8(*pMeshObject, name.c_str());
223 if(hResult != LIB3MF_OK)
224 {
225 std::cerr << "could not set object name: " << std::hex << hResult << std::endl;
226 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
227 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
228 NMR::lib3mf_release(*pMeshObject);
229 NMR::lib3mf_release(pModel);
230 return false;
231 }
232
233 //add a builditem to finish
234 return add_build_item(pModel, *pMeshObject);
235 }
236
237 //remember that it adds 3 demmy vertices in the beginning, and a dummy triangle to be ignored.
238 template<typename PointRange, typename Color>
write_points(const PointRange & points,const Color & color,const std::string & name,NMR::PLib3MFModelMeshObject ** pMeshObject,NMR::PLib3MFModel * pModel)239 bool write_points(const PointRange& points,
240 const Color& color,
241 const std::string& name,
242 NMR::PLib3MFModelMeshObject** pMeshObject,
243 NMR::PLib3MFModel * pModel)
244 {
245 DWORD nErrorMessage;
246 LPCSTR pszErrorMessage;
247 HRESULT hResult;
248
249 // Create mesh structure
250 std::vector<NMR::MODELMESHVERTEX> pVertices;
251
252 // Create Mesh Object
253 hResult = NMR::lib3mf_model_addmeshobject(pModel, pMeshObject);
254 if(hResult != LIB3MF_OK)
255 {
256 std::cerr << "could not add mesh object: " << std::hex << hResult << std::endl;
257 NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage);
258 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
259 NMR::lib3mf_release(pModel);
260 return false;
261 }
262
263 //add 3 dummy vertices to be sure to have a valid triangle and accept point sets with fewer than 3 vertices.
264 for(int i = 0; i< 3; ++i)
265 pVertices.push_back(tmf_internal::fnCreateVertex(0,0,0));
266
267 for(const auto& point : points)
268 pVertices.push_back(tmf_internal::fnCreateVertex(point.x(), point.y(), point.z()));
269
270 NMR::MODELMESHTRIANGLE dummy_triangle = tmf_internal::fnCreateTriangle(0,1,2); //add a triangle to avoid lib error.
271 hResult = NMR::lib3mf_meshobject_setgeometry(*pMeshObject, pVertices.data(),
272 pVertices.size(), &dummy_triangle, 1);
273 if(hResult != LIB3MF_OK)
274 {
275 std::cerr << "could not set mesh geometry: " << std::hex << hResult << std::endl;
276 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
277 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
278 NMR::lib3mf_release(*pMeshObject);
279 NMR::lib3mf_release(pModel);
280 return false;
281 }
282
283 // Create color entries
284 NMR::PLib3MFPropertyHandler * pPropertyHandler;
285 hResult = NMR::lib3mf_meshobject_createpropertyhandler(*pMeshObject, &pPropertyHandler);
286 if(hResult != LIB3MF_OK)
287 {
288 std::cerr << "could not create property handler: " << std::hex << hResult << std::endl;
289 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
290 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
291 NMR::lib3mf_release(*pMeshObject);
292 NMR::lib3mf_release(pModel);
293 return false;
294 }
295
296 // define colors
297 NMR::MODELMESHCOLOR_SRGB sColor = tmf_internal::fnCreateColor (color.red(),
298 color.green(),
299 color.blue(),
300 color.alpha());
301 // One-colored Triangles
302 NMR::lib3mf_propertyhandler_setsinglecolor(pPropertyHandler, 0, &sColor);
303
304 // make sure to define a default property
305 NMR::PLib3MFDefaultPropertyHandler * pDefaultPropertyHandler;
306 hResult = NMR::lib3mf_object_createdefaultpropertyhandler(*pMeshObject, &pDefaultPropertyHandler);
307 if(hResult != LIB3MF_OK)
308 {
309 std::cerr<< "could not create default property handler: " << std::hex << hResult << std::endl;
310 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
311 std::cerr<< "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
312 NMR::lib3mf_release(*pMeshObject);
313 NMR::lib3mf_release(pModel);
314 return false;
315 }
316
317 NMR::MODELMESHCOLOR_SRGB default_color = tmf_internal::fnCreateColor(0,0,0,0);
318 NMR::lib3mf_defaultpropertyhandler_setcolor(pDefaultPropertyHandler,
319 &default_color);
320
321 // release default property handler
322 NMR::lib3mf_release(pDefaultPropertyHandler);
323
324 // Set name
325 hResult = NMR::lib3mf_object_setnameutf8(*pMeshObject, name.c_str());
326 if(hResult != LIB3MF_OK)
327 {
328 std::cerr << "could not set object name: " << std::hex << hResult << std::endl;
329 NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage);
330 std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
331 NMR::lib3mf_release(*pMeshObject);
332 NMR::lib3mf_release(pModel);
333 return false;
334 }
335
336 return add_build_item(pModel, *pMeshObject);
337 }
338
339 template<typename PointRange, typename Color>
write_point_cloud_to_model(const PointRange & points,const Color & color,const std::string & name,NMR::PLib3MFModelMeshObject ** pMeshObject,NMR::PLib3MFModel * pModel)340 bool write_point_cloud_to_model(const PointRange& points,
341 const Color& color,
342 const std::string& name,
343 NMR::PLib3MFModelMeshObject** pMeshObject,
344 NMR::PLib3MFModel * pModel)
345 {
346 std::string pc_name = name;
347 pc_name.append("_cgal_pc");
348 return write_points(points, color, pc_name, pMeshObject, pModel);
349 }
350
351 template<typename PointRange, typename Color>
write_polyline_to_model(const PointRange & points,const Color & color,const std::string & name,NMR::PLib3MFModelMeshObject ** pMeshObject,NMR::PLib3MFModel * pModel)352 bool write_polyline_to_model(const PointRange& points,
353 const Color& color,
354 const std::string& name,
355 NMR::PLib3MFModelMeshObject** pMeshObject,
356 NMR::PLib3MFModel * pModel)
357 {
358 std::string pc_name = name;
359 pc_name.append("_cgal_pl");
360 return write_points(points, color, pc_name, pMeshObject, pModel);
361 }
362
363 } // namespace IO
364 } // namespace CGAL
365
366 #endif // CGAL_LINKED_WITH_3MF
367
368 #endif // CGAL_IO_WRITE_3MF_H
369