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