1 //-----------------------------------------------------------------------------
2 // Product:     OpenCTM tools
3 // File:        ply.cpp
4 // Description: Implementation of the PLY file format importer/exporter.
5 //-----------------------------------------------------------------------------
6 // Copyright (c) 2009-2010 Marcus Geelnard
7 //
8 // This software is provided 'as-is', without any express or implied
9 // warranty. In no event will the authors be held liable for any damages
10 // arising from the use of this software.
11 //
12 // Permission is granted to anyone to use this software for any purpose,
13 // including commercial applications, and to alter it and redistribute it
14 // freely, subject to the following restrictions:
15 //
16 //     1. The origin of this software must not be misrepresented; you must not
17 //     claim that you wrote the original software. If you use this software
18 //     in a product, an acknowledgment in the product documentation would be
19 //     appreciated but is not required.
20 //
21 //     2. Altered source versions must be plainly marked as such, and must not
22 //     be misrepresented as being the original software.
23 //
24 //     3. This notice may not be removed or altered from any source
25 //     distribution.
26 //-----------------------------------------------------------------------------
27 
28 #include <iostream>
29 #include <stdexcept>
30 #include <string>
31 #include <fstream>
32 #include <iomanip>
33 #include <sstream>
34 #include <vector>
35 #include <clocale>
36 #include <rply.h>
37 #include "ply.h"
38 #include "common.h"
39 
40 using namespace std;
41 
42 typedef struct {
43   Mesh * mMesh;
44   long mFaceIdx;
45   long mVertexIdx;
46   long mNormalIdx;
47   long mTexCoordIdx;
48   long mColorIdx;
49 } PLYReaderState;
50 
51 
PLYFaceCallback(p_ply_argument argument)52 static int PLYFaceCallback(p_ply_argument argument)
53 {
54   PLYReaderState * state;
55   long dummy, length, valueIndex;
56   ply_get_argument_user_data(argument, (void **) &state, &dummy);
57   double value = ply_get_argument_value(argument);
58   ply_get_argument_property(argument, NULL, &length, &valueIndex);
59   if((valueIndex >= 0) && (valueIndex <= 2))
60     state->mMesh->mIndices[state->mFaceIdx * 3 + valueIndex] = int(value);
61   if(valueIndex == 2)
62     ++ state->mFaceIdx;
63   return 1;
64 }
65 
PLYVertexCallback(p_ply_argument argument)66 static int PLYVertexCallback(p_ply_argument argument)
67 {
68   PLYReaderState * state;
69   long index;
70   ply_get_argument_user_data(argument, (void **) &state, &index);
71   double value = ply_get_argument_value(argument);
72   switch(index)
73   {
74     case 0:
75       state->mMesh->mVertices[state->mVertexIdx].x = float(value);
76       break;
77     case 1:
78       state->mMesh->mVertices[state->mVertexIdx].y = float(value);
79       break;
80     case 2:
81       state->mMesh->mVertices[state->mVertexIdx].z = float(value);
82       ++ state->mVertexIdx;
83       break;
84   }
85   return 1;
86 }
87 
PLYNormalCallback(p_ply_argument argument)88 static int PLYNormalCallback(p_ply_argument argument)
89 {
90   PLYReaderState * state;
91   long index;
92   ply_get_argument_user_data(argument, (void **) &state, &index);
93   double value = ply_get_argument_value(argument);
94   switch(index)
95   {
96     case 0:
97       state->mMesh->mNormals[state->mNormalIdx].x = float(value);
98       break;
99     case 1:
100       state->mMesh->mNormals[state->mNormalIdx].y = float(value);
101       break;
102     case 2:
103       state->mMesh->mNormals[state->mNormalIdx].z = float(value);
104       ++ state->mNormalIdx;
105       break;
106   }
107   return 1;
108 }
109 
PLYTexCoordCallback(p_ply_argument argument)110 static int PLYTexCoordCallback(p_ply_argument argument)
111 {
112   PLYReaderState * state;
113   long index;
114   ply_get_argument_user_data(argument, (void **) &state, &index);
115   double value = ply_get_argument_value(argument);
116   switch(index)
117   {
118     case 0:
119       state->mMesh->mTexCoords[state->mTexCoordIdx].u = float(value);
120       break;
121     case 1:
122       state->mMesh->mTexCoords[state->mTexCoordIdx].v = float(value);
123       ++ state->mTexCoordIdx;
124       break;
125   }
126   return 1;
127 }
128 
PLYColorCallback(p_ply_argument argument)129 static int PLYColorCallback(p_ply_argument argument)
130 {
131   PLYReaderState * state;
132   long index;
133   ply_get_argument_user_data(argument, (void **) &state, &index);
134   double value = ply_get_argument_value(argument);
135   switch(index)
136   {
137     case 0:
138       state->mMesh->mColors[state->mColorIdx].x = float(value) / 255.0f;
139       break;
140     case 1:
141       state->mMesh->mColors[state->mColorIdx].y = float(value) / 255.0f;
142       break;
143     case 2:
144       state->mMesh->mColors[state->mColorIdx].z = float(value) / 255.0f;
145       ++ state->mColorIdx;
146       break;
147   }
148   return 1;
149 }
150 
151 /// Import a PLY file from a file.
Import_PLY(const char * aFileName,Mesh * aMesh)152 void Import_PLY(const char * aFileName, Mesh * aMesh)
153 {
154   // Start by ensuring that we use proper locale settings for the file format
155   setlocale(LC_NUMERIC, "C");
156 
157   // Clear the mesh
158   aMesh->Clear();
159 
160   // Initialize the state
161   PLYReaderState state;
162   state.mMesh = aMesh;
163   state.mFaceIdx = 0;
164   state.mVertexIdx = 0;
165   state.mNormalIdx = 0;
166   state.mTexCoordIdx = 0;
167   state.mColorIdx = 0;
168 
169   // Open the PLY file
170   p_ply ply = ply_open(aFileName, NULL);
171   if(!ply)
172     throw runtime_error("Unable to open PLY file.");
173   if(!ply_read_header(ply))
174     throw runtime_error("Invalid PLY file.");
175 
176   // Get the file comment (if any)
177   bool firstComment = true;
178   const char * comment = ply_get_next_comment(ply, NULL);
179   while(comment)
180   {
181     if(firstComment)
182       aMesh->mComment = string(comment);
183     else
184       aMesh->mComment += string(" ") + string(comment);
185     firstComment = false;
186     comment = ply_get_next_comment(ply, comment);
187   }
188 
189   // Set face callback
190   long faceCount = ply_set_read_cb(ply, "face", "vertex_indices", PLYFaceCallback, &state, 0);
191   if(faceCount == 0)
192     faceCount = ply_set_read_cb(ply, "face", "vertex_index", PLYFaceCallback, &state, 0);
193 
194   // Set vertex callback
195   long vertexCount = ply_set_read_cb(ply, "vertex", "x", PLYVertexCallback, &state, 0);
196   ply_set_read_cb(ply, "vertex", "y", PLYVertexCallback, &state, 1);
197   ply_set_read_cb(ply, "vertex", "z", PLYVertexCallback, &state, 2);
198 
199   // Set normal callback
200   long normalCount = ply_set_read_cb(ply, "vertex", "nx", PLYNormalCallback, &state, 0);
201   ply_set_read_cb(ply, "vertex", "ny", PLYNormalCallback, &state, 1);
202   ply_set_read_cb(ply, "vertex", "nz", PLYNormalCallback, &state, 2);
203 
204   // Set tex coord callback
205   long texCoordCount = ply_set_read_cb(ply, "vertex", "s", PLYTexCoordCallback, &state, 0);
206   ply_set_read_cb(ply, "vertex", "t", PLYTexCoordCallback, &state, 1);
207 
208   // Set color callback
209   long colorCount = ply_set_read_cb(ply, "vertex", "red", PLYColorCallback, &state, 0);
210   ply_set_read_cb(ply, "vertex", "green", PLYColorCallback, &state, 1);
211   ply_set_read_cb(ply, "vertex", "blue", PLYColorCallback, &state, 2);
212 
213   // Sanity check
214   if((faceCount < 1) || (vertexCount < 1))
215     throw runtime_error("Empty PLY mesh - invalid file format?");
216 
217   // Prepare the mesh
218   aMesh->mIndices.resize(faceCount * 3);
219   aMesh->mVertices.resize(vertexCount);
220   aMesh->mNormals.resize(normalCount);
221   aMesh->mTexCoords.resize(texCoordCount);
222   aMesh->mColors.resize(colorCount);
223 
224   // Read the PLY file
225   if(!ply_read(ply))
226     throw runtime_error("Unable to load PLY file.");
227 
228   // Close the PLY file
229   ply_close(ply);
230 }
231 
232 /// Export a PLY file to a file.
Export_PLY(const char * aFileName,Mesh * aMesh,Options & aOptions)233 void Export_PLY(const char * aFileName, Mesh * aMesh, Options &aOptions)
234 {
235   // Start by ensuring that we use proper locale settings for the file format
236   setlocale(LC_NUMERIC, "C");
237 
238   // What should we export?
239   bool exportTexCoords = aMesh->HasTexCoords() && !aOptions.mNoTexCoords;
240   bool exportNormals = aMesh->HasNormals() && !aOptions.mNoNormals;
241   bool exportColors = aMesh->HasColors() && !aOptions.mNoColors;
242 
243   // Open the output file
244   ofstream f(aFileName, ios_base::out | ios_base::binary);
245   if(f.fail())
246     throw runtime_error("Could not open output file.");
247 
248   // Set floating point precision
249   f << setprecision(8);
250 
251   // Write header
252   f << "ply" << endl;
253   f << "format ascii 1.0" << endl;
254   if(aMesh->mComment.size() > 0)
255   {
256     stringstream sstr(aMesh->mComment);
257     sstr.seekg(0);
258     while(!sstr.eof())
259     {
260       string line;
261       getline(sstr, line);
262       line = TrimString(line);
263       if(line.size() > 0)
264         f << "comment " << line << endl;
265     }
266   }
267   f << "element vertex " << aMesh->mVertices.size() << endl;
268   f << "property float x" << endl;
269   f << "property float y" << endl;
270   f << "property float z" << endl;
271   if(exportTexCoords)
272   {
273     f << "property float s" << endl;
274     f << "property float t" << endl;
275   }
276   if(exportNormals)
277   {
278     f << "property float nx" << endl;
279     f << "property float ny" << endl;
280     f << "property float nz" << endl;
281   }
282   if(exportColors)
283   {
284     f << "property uchar red" << endl;
285     f << "property uchar green" << endl;
286     f << "property uchar blue" << endl;
287   }
288   f << "element face " << aMesh->mIndices.size() / 3 << endl;
289   f << "property list uchar int vertex_indices" << endl;
290   f << "end_header" << endl;
291 
292   // Write vertices
293   for(unsigned int i = 0; i < aMesh->mVertices.size(); ++ i)
294   {
295     f << aMesh->mVertices[i].x << " " <<
296          aMesh->mVertices[i].y << " " <<
297          aMesh->mVertices[i].z;
298     if(exportTexCoords)
299       f << " " << aMesh->mTexCoords[i].u << " " <<
300                   aMesh->mTexCoords[i].v;
301     if(exportNormals)
302       f << " " << aMesh->mNormals[i].x << " " <<
303                   aMesh->mNormals[i].y << " " <<
304                   aMesh->mNormals[i].z;
305     if(exportColors)
306       f << " " << int(floorf(255.0f * aMesh->mColors[i].x + 0.5f)) << " " <<
307                   int(floorf(255.0f * aMesh->mColors[i].y + 0.5f)) << " " <<
308                   int(floorf(255.0f * aMesh->mColors[i].z + 0.5f));
309     f << endl;
310   }
311 
312   // Write faces
313   for(unsigned int i = 0; i < aMesh->mIndices.size() / 3; ++ i)
314     f << "3 " << aMesh->mIndices[i * 3] << " " <<
315                  aMesh->mIndices[i * 3 + 1] << " " <<
316                  aMesh->mIndices[i * 3 + 2] << endl;
317 
318   // Close the output file
319   f.close();
320 }
321