comparison dmmodel.cpp @ 19:a329f0216491

Implement PLY file format parsing and extremely simplistic scene setup file format. Not finished yet.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 07 Nov 2019 20:15:33 +0200
parents
children 1404dfcee7b8
comparison
equal deleted inserted replaced
18:b1e75c65016d 19:a329f0216491
1 //
2 //
3 //
4 #include "dmmodel.h"
5
6
7 static bool dmError(DMTextFileInfo &info, const std::string &msg)
8 {
9 printf("ERROR: %s on line #%d: %s\n",
10 msg.c_str(), info.nline, info.line.c_str());
11 return false;
12 }
13
14
15 static bool dmSyntaxError(DMTextFileInfo &info, const std::string &msg)
16 {
17 printf("ERROR: Syntax error on line #%d: %s\n",
18 info.nline, msg.c_str());
19 return false;
20 }
21
22
23 static bool dmReadLine(DMTextFileInfo &info)
24 {
25 if (!std::getline(info.file, info.line))
26 return false;
27
28 info.nline++;
29
30 info.line = dmStrTrim(info.line);
31
32 return true;
33 }
34
35
36 static DMPLYPropType dmPLYParsePropType(const std::string &name)
37 {
38 if (name == "list")
39 return PLY_TYPE_LIST;
40 else
41 if (name == "float" || name == "float32")
42 return PLY_TYPE_FLOAT;
43 else
44 if (name == "double" || name == "float64")
45 return PLY_TYPE_DOUBLE;
46 else
47 if (name == "uchar" || name == "uint8")
48 return PLY_TYPE_UINT8;
49 else
50 if (name == "int16")
51 return PLY_TYPE_INT16;
52 else
53 if (name == "uint16")
54 return PLY_TYPE_UINT16;
55 else
56 if (name == "int" || name == "int32")
57 return PLY_TYPE_INT32;
58 else
59 if (name == "uint" || name == "uint32")
60 return PLY_TYPE_UINT32;
61 else
62 return PLY_TYPE_NONE;
63 }
64
65
66 static bool dmPLYParsePropertyValueASCII(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval, size_t &pos)
67 {
68 size_t len = 0;
69 std::string val = info.line.substr(pos);
70
71 switch (ptype)
72 {
73 case PLY_TYPE_INT8:
74 case PLY_TYPE_INT16:
75 case PLY_TYPE_INT32:
76 pval.v_int = std::stoi(val, &len);
77 break;
78
79 case PLY_TYPE_UINT8:
80 case PLY_TYPE_UINT16:
81 case PLY_TYPE_UINT32:
82 pval.v_uint = std::stoi(val, &len);
83 break;
84
85 case PLY_TYPE_FLOAT:
86 pval.v_float = std::stof(val, &len);
87 break;
88
89 case PLY_TYPE_DOUBLE:
90 pval.v_double = std::stod(val, &len);
91 break;
92
93 default:
94 return dmError(info,
95 "Internal error, unimplemented PLY property type");
96 }
97
98 pos += len;
99
100 return true;
101 }
102
103
104 static bool dmPLYParsePropertyASCII(DMPLYFileInfo &info, DMPLYFileProperty &prop, size_t &pos)
105 {
106 switch (prop.type)
107 {
108 case PLY_TYPE_LIST:
109 prop.list_values.clear();
110
111 if (!dmPLYParsePropertyValueASCII(info, prop.list_num_type, prop.list_num_value, pos))
112 return false;
113
114 for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++)
115 {
116 DMPLYPropValue pval;
117 if (!dmPLYParsePropertyValueASCII(info, prop.list_values_type, pval, pos))
118 return false;
119
120 prop.list_values.push_back(pval);
121 }
122
123 if (prop.list_values.size() != prop.list_num_value.v_uint)
124 {
125 return dmSyntaxError(info,
126 "Number of property list '"+ prop.name +" values not equal to number specified");
127 }
128 return true;
129
130 default:
131 return dmPLYParsePropertyValueASCII(info, prop.type, prop.value, pos);
132 }
133 }
134
135
136 static bool dmPLYParseElementASCII(DMPLYFileInfo &info, DMPLYFileElement &element)
137 {
138 size_t nprop = 0, pos = 0;
139
140 // Read one line
141 if (!dmReadLine(info))
142 return false;
143
144 // Skip empty lines
145 if (info.line.empty())
146 return true;
147
148 // Parse the properties
149 for (auto const prop : element.properties)
150 {
151 if (!dmPLYParsePropertyASCII(info, *prop, pos))
152 return false;
153
154 nprop++;
155 }
156
157 if (nprop != element.properties.size())
158 {
159 return dmSyntaxError(info,
160 "Expected N properties, got different number");
161 }
162
163 return true;
164 }
165
166
167 bool dmPLYReadPropertyValueBIN(DMPLYFileInfo &info, const DMPLYPropType ptype, DMPLYPropValue &pval)
168 {
169 uint8_t tmp[8];
170 uint16_t tmpU16;
171 int16_t tmpS16;
172 uint32_t tmpU32;
173 int32_t tmpS32;
174
175 switch (ptype)
176 {
177 case PLY_TYPE_INT8:
178 info.file.read(reinterpret_cast<char *>(&tmp), 1);
179 pval.v_int = (int8_t) tmp[0];
180 break;
181
182 case PLY_TYPE_UINT8:
183 info.file.read(reinterpret_cast<char *>(&tmp), 1);
184 pval.v_uint = (uint8_t) tmp[0];
185 break;
186
187 case PLY_TYPE_INT16:
188 info.file.read(reinterpret_cast<char *>(&tmp), 2);
189 tmpS16 =
190 (tmp[0] << 8) |
191 (tmp[1]);
192 pval.v_int = tmpS16;
193 break;
194
195 case PLY_TYPE_UINT16:
196 info.file.read(reinterpret_cast<char *>(&tmp), 2);
197 tmpU16 =
198 (tmp[0] << 8) |
199 (tmp[1]);
200 pval.v_uint = tmpU16;
201 break;
202
203 case PLY_TYPE_INT32:
204 info.file.read(reinterpret_cast<char *>(&tmp), 4);
205 tmpS32 =
206 (tmp[0] << 24) |
207 (tmp[1] << 16) |
208 (tmp[2] << 8) |
209 (tmp[3]);
210
211 pval.v_int = tmpS32;
212 break;
213
214 case PLY_TYPE_UINT32:
215 info.file.read(reinterpret_cast<char *>(&tmp), 4);
216 tmpU32 =
217 (tmp[3] << 24) |
218 (tmp[2] << 16) |
219 (tmp[1] << 8) |
220 (tmp[0]);
221
222 pval.v_uint = tmpU32;
223 break;
224
225 case PLY_TYPE_FLOAT:
226 info.file.read(reinterpret_cast<char *>(&tmp), 4);
227 tmpU32 =
228 (tmp[0] << 24) |
229 (tmp[1] << 16) |
230 (tmp[2] << 8) |
231 (tmp[3]);
232
233 pval.v_uint = tmpU32;
234 break;
235
236 default:
237 return dmError(info,
238 "Internal error, unimplemented PLY property type");
239 }
240
241 return true;
242 }
243
244
245 bool dmPLYReadPropertyBIN(DMPLYFileInfo &info, DMPLYFileProperty &prop)
246 {
247 switch (prop.type)
248 {
249 case PLY_TYPE_LIST:
250 prop.list_values.clear();
251
252 if (!dmPLYReadPropertyValueBIN(info, prop.list_num_type, prop.list_num_value))
253 return false;
254
255 for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++)
256 {
257 DMPLYPropValue pval;
258 if (!dmPLYReadPropertyValueBIN(info, prop.list_values_type, pval))
259 return false;
260
261 prop.list_values.push_back(pval);
262 }
263
264 if (prop.list_values.size() != prop.list_num_value.v_uint)
265 {
266 return dmSyntaxError(info,
267 "Number of property list '"+ prop.name +" values not equal to number specified");
268 }
269 return true;
270
271 default:
272 return dmPLYReadPropertyValueBIN(info, prop.type, prop.value);
273 }
274 }
275
276
277 bool dmPLYReadElementBIN(DMPLYFileInfo &info, DMPLYFileElement &element)
278 {
279 size_t nprop = 0;
280
281 for (auto const prop : element.properties)
282 {
283 if (!dmPLYReadPropertyBIN(info, *prop))
284 return false;
285
286 nprop++;
287 }
288
289 if (nprop != element.properties.size())
290 {
291 return dmSyntaxError(info,
292 "Expected N properties, got different number");
293 }
294
295 return true;
296 }
297
298
299 bool DMModel::loadFromPLY(const std::string &filename)
300 {
301 DMPLYFileInfo info;
302
303 return loadFromPLY(filename, info);
304 }
305
306
307 bool DMModel::loadFromPLY(const std::string &filename, DMPLYFileInfo &info)
308 {
309 info.filename = filename;
310 info.nline = info.state = 0;
311 info.file.open(info.filename.c_str(), std::fstream::in | std::fstream::binary);
312
313 printf("INFO: Trying to read mesh from '%s'.\n",
314 info.filename.c_str());
315
316 if (!info.file.is_open())
317 {
318 printf("ERROR: Unable to open file '%s'.\n",
319 info.filename.c_str());
320 return false;
321 }
322
323 // Parse the PLY header
324 while (info.state >= 0)
325 {
326 // Read one line
327 if (!dmReadLine(info))
328 return false;
329
330 // Skip empty lines
331 if (info.line.empty())
332 continue;
333
334 // Split key and value
335 std::vector<std::string> tokens = dmStrSplit(info.line);
336 std::string &key = tokens[0];
337
338 if (info.state == 0 && key == "ply")
339 {
340 info.state = 1;
341 }
342 else
343 if (info.state > 0 && key == "end_header")
344 {
345 info.state = -1;
346 }
347 else
348 if (info.state == 1 && key == "format")
349 {
350 if (tokens.size() < 3)
351 {
352 return dmSyntaxError(info,
353 "Expected value for format");
354 }
355
356 info.format = PLY_FMT_UNKNOWN;
357
358 if (tokens[1] == "ascii")
359 info.format = PLY_FMT_ASCII;
360 else
361 if (tokens[1] == "binary_little_endian")
362 info.format = PLY_FMT_BIN_LE;
363 else
364 if (tokens[1] == "binary_big_endian")
365 info.format = PLY_FMT_BIN_BE;
366
367 if (info.format == PLY_FMT_UNKNOWN ||
368 tokens[2] != "1.0")
369 {
370 printf("ERROR: Unknown or unsupported PLY file format '%s'.\n",
371 (tokens[1] +" "+ tokens[2]).c_str());
372 return false;
373 }
374
375 info.state = 2;
376 }
377 else
378 if ((info.state == 2 || info.state == 3) && key == "element")
379 {
380 if (tokens.size() < 3)
381 {
382 return dmSyntaxError(info,
383 "Expected a value for element key");
384 }
385
386 std::string &el_name = tokens[1], &el_value = tokens[2];
387
388 if (info.elem_map.count(el_name))
389 {
390 return dmSyntaxError(info,
391 "Element '"+ el_name +"' has already been defined");
392 }
393
394 DMPLYFileElement &elem = info.elem_map[el_name];
395 info.element = &elem;
396 info.elements.push_back(&elem);
397
398 elem.name = el_name;
399 elem.value = std::stoi(el_value);
400
401 info.state = 3;
402 }
403 else
404 if (info.state == 3 && key == "property")
405 {
406 if (tokens.size() < 3)
407 {
408 return dmSyntaxError(info,
409 "Expected value for property");
410 }
411
412 const std::string
413 &pr_name = tokens.back(),
414 &pr_typename = tokens.at(1);
415
416 if (!info.element)
417 {
418 // Should not happen
419 return dmSyntaxError(info,
420 "No element defined for property '"+ pr_name +"'?");
421 }
422
423 // Check if this property has been already defined
424 if (info.element->prop_map.count(pr_name))
425 {
426 return dmSyntaxError(info,
427 "Element '"+ info.element->name +
428 "' already has property '"+ pr_name +"' defined?");
429 }
430
431 // Parse property information
432 DMPLYPropType pr_type = dmPLYParsePropType(pr_typename);
433 if (pr_type == PLY_TYPE_NONE)
434 {
435 return dmSyntaxError(info,
436 "Invalid or unsupported property type '"+ pr_typename +"'");
437 }
438
439 DMPLYFileProperty &prop = info.element->prop_map[pr_name];
440 info.element->properties.push_back(&prop);
441
442 prop.name = pr_name;
443 prop.type = pr_type;
444
445 if (pr_type == PLY_TYPE_LIST)
446 {
447 // List is a special case
448 if (tokens.size() < 5)
449 {
450 return dmSyntaxError(info,
451 "Expected more values for a list property (num_type, val_type, name)");
452 }
453
454 prop.list_num_type = dmPLYParsePropType(tokens.at(2));
455 prop.list_values_type = dmPLYParsePropType(tokens.at(3));
456
457 if (prop.list_num_type == PLY_TYPE_NONE ||
458 prop.list_values_type == PLY_TYPE_NONE)
459 {
460 return dmSyntaxError(info,
461 "Invalid or unsupported property type(s)");
462 }
463 }
464 }
465 else
466 if (info.state > 0 // && (key == "comment" || key == "obj_info")
467 )
468 {
469 // Ignore comments
470 // .. and unknown keys
471 }
472 else
473 {
474 return dmSyntaxError(info,
475 "Unexpected key '"+ key +"'");
476 }
477 }
478
479 // Check header data
480 DMPLYFileElement *elem;
481 DMPLYFileProperty *prop;
482 if (info.state != -1 ||
483 (elem = info.checkElem(PLY_ELEM_FACE)) == 0 ||
484 (prop = elem->checkProp(PLY_PROP_VERTEX_INDICES)) == 0 ||
485 prop->type != PLY_TYPE_LIST ||
486 (elem = info.checkElem(PLY_ELEM_VERTEX)) == 0 ||
487 (prop = elem->checkProp("x")) == 0 || prop->type != PLY_TYPE_FLOAT ||
488 (prop = elem->checkProp("y")) == 0 || prop->type != PLY_TYPE_FLOAT ||
489 (prop = elem->checkProp("z")) == 0 || prop->type != PLY_TYPE_FLOAT
490 )
491 {
492 printf("ERROR: PLY file did not contain expected information.\n");
493 return false;
494 }
495
496 nvertices = info.elem_map[PLY_ELEM_VERTEX].value;
497 nfaces = info.elem_map[PLY_ELEM_FACE].value;
498
499
500 if (nvertices < 3 || nfaces < 1)
501 {
502 printf("ERROR: Invalid nvertices (%d) and/or nfaces (%d).\n",
503 nvertices, nfaces);
504 return false;
505 }
506
507 printf("INFO: Should have %d vertices, %d faces\n",
508 nvertices, nfaces);
509
510 // Pre-allocate space
511 vertices.reserve(nvertices);
512 normals.reserve(nvertices);
513 faces.reserve(nfaces * 3);
514
515 // Read the actual data (in order of the elements)
516 for (auto *element : info.elements)
517 for (int n = 0; n < element->value; n++)
518 {
519 switch (info.format)
520 {
521 case PLY_FMT_ASCII:
522 if (!dmPLYParseElementASCII(info, *element))
523 return false;
524 break;
525
526 default:
527 if (!dmPLYReadElementBIN(info, *element))
528 return false;
529 break;
530 }
531
532 // Check for specific elements
533 if (element->name == PLY_ELEM_FACE)
534 {
535 DMPLYFileProperty &prop = element->prop_map[PLY_PROP_VERTEX_INDICES];
536
537 if (prop.list_num_value.v_uint != 3)
538 {
539 return dmSyntaxError(info,
540 "Expected 3 vertices per face");
541 }
542
543 for (unsigned int n = 0; n < prop.list_num_value.v_uint; n++)
544 {
545 faces.push_back(prop.list_values[n].v_uint);
546 }
547 }
548 else
549 if (element->name == PLY_ELEM_VERTEX)
550 {
551 DMVertex vert;
552 vert.x = element->prop_map["x"].value.v_float;
553 vert.y = element->prop_map["y"].value.v_float;
554 vert.z = element->prop_map["z"].value.v_float;
555
556 vertices.push_back(vert);
557
558 if (element->checkProp("nx") &&
559 element->checkProp("ny") &&
560 element->checkProp("nz"))
561 {
562 DMVertex normal;
563 normal.x = element->prop_map["nx"].value.v_float;
564 normal.y = element->prop_map["ny"].value.v_float;
565 normal.z = element->prop_map["nz"].value.v_float;
566
567 normals.push_back(normal);
568 }
569 }
570 }
571
572 printf("INFO: Found %ld vertices, %ld normals, %ld faces\n",
573 vertices.size(),
574 normals.size(),
575 faces.size() / 3);
576
577 return true;
578 }
579
580
581 bool DMSimpleScene::loadInfo(const std::string &filename)
582 {
583 DMTextFileInfo info;
584
585 info.filename = filename;
586 info.nline = info.state = 0;
587 info.file.open(info.filename.c_str(), std::fstream::in | std::fstream::binary);
588
589 printf("INFO: Trying to read scene data from '%s'.\n",
590 info.filename.c_str());
591
592 if (!info.file.is_open())
593 {
594 printf("ERROR: Unable to open file '%s'.\n",
595 info.filename.c_str());
596 return false;
597 }
598
599 while (info.state >= 0)
600 {
601 // Read one line
602 if (!dmReadLine(info))
603 {
604 info.state = -1;
605 break;
606 }
607
608 // Skip empty lines and comments
609 if (info.line.empty() ||
610 info.line[0] == '#' ||
611 info.line[0] == ';')
612 continue;
613
614 // Split key and values
615 std::vector<std::string> tokens = dmStrSplit(info.line);
616 std::string &key = tokens[0];
617
618 if (key == "color")
619 {
620 DMColor color;
621 color.alpha = 0xff;
622
623 if (tokens.size() == 2)
624 {
625 color.r = color.g = color.b = std::stoi(tokens[1], 0, 0);
626 }
627 else
628 if (tokens.size() == 4 || tokens.size() == 5)
629 {
630 color.r = std::stoi(tokens[1], 0, 0);
631 color.g = std::stoi(tokens[2], 0, 0);
632 color.b = std::stoi(tokens[3], 0, 0);
633
634 if (tokens.size() == 5)
635 color.alpha = std::stoi(tokens[4], 0, 0);
636 }
637 else
638 {
639 return dmSyntaxError(info,
640 "Expected color values <value> or <red> <green> <blue> [<alpha>]");
641 }
642
643 model.color = color;
644 }
645 else
646 if (key == "translate" || key == "rotate" || key == "scale" ||
647 key == "camera_pos" || key == "camera_at")
648 {
649 DMVertex vec;
650
651 if (tokens.size() == 2)
652 {
653 vec.x = vec.y = vec.z = std::stof(tokens[1]);
654 }
655 else
656 if (tokens.size() == 4)
657 {
658 vec.x = std::stof(tokens[1]);
659 vec.y = std::stof(tokens[2]);
660 vec.z = std::stof(tokens[3]);
661 }
662 else
663 {
664 return dmSyntaxError(info,
665 "Expected vector value for '"+ key +"'");
666 }
667
668 if (key == "translate")
669 model.translate = vec;
670 else
671 if (key == "rotate")
672 model.rotate = vec;
673 else
674 if (key == "scale")
675 model.scale = vec;
676 else
677 if (key == "camera_pos")
678 camera.pos = vec;
679 else
680 if (key == "camera_at")
681 camera.lookAt = vec;
682 }
683 else
684 if (key == "light")
685 {
686 if (tokens.size() == 4)
687 {
688 DMVertex pos;
689 pos.x = std::stof(tokens[1]);
690 pos.y = std::stof(tokens[2]);
691 pos.z = std::stof(tokens[3]);
692 }
693 else
694 {
695 return dmSyntaxError(info,
696 "Expected light definition as <x> <y> <z> ");
697 }
698 }
699 else
700 {
701 return dmSyntaxError(info,
702 "Unexpected key '"+ key +"'");
703 }
704 }
705
706 model.printInfo();
707
708 return true;
709 }