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