├── .gitignore ├── README.md ├── assets ├── archive │ ├── ascii_read.png │ ├── ascii_read_times.md │ ├── ascii_write.png │ ├── ascii_write_times.md │ ├── little_endian_read.png │ ├── little_endian_read_times.md │ ├── little_endian_write.png │ └── little_endian_write_times.md ├── ascii_read_avg_table.md ├── ascii_read_fig.png ├── ascii_read_full_table.md ├── ascii_write_avg_table.md ├── ascii_write_fig.png ├── ascii_write_full_table.md ├── binary_read_avg_table.md ├── binary_read_fig.png ├── binary_read_full_table.md ├── binary_write_avg_table.md ├── binary_write_fig.png └── binary_write_full_table.md ├── benchmark ├── compute_results.py ├── run_benchmark.py └── run_test.py ├── build.bat ├── makefile ├── plylibs ├── happly │ └── happly.h ├── micro_ply │ └── micro_ply.h ├── miniply │ ├── miniply.cpp │ └── miniply.h ├── nanoply │ └── nanoply.hpp ├── plylib │ ├── plylib.cpp │ └── plylib.h ├── rply │ ├── rply.c │ ├── rply.h │ └── rplyfile.h ├── tinyply21 │ ├── tinyply.cpp │ └── tinyply.h ├── tinyply22 │ └── tinyply.h ├── tinyply23 │ ├── tinyply.cpp │ └── tinyply.h └── turkply │ ├── ply_io.c │ └── ply_io.h └── tests ├── base_test.h ├── happly_test.cpp ├── microply_test.cpp ├── miniply_test.cpp ├── mshply_test.c ├── nanoply_test.cpp ├── plylib_test.cpp ├── rply_test.c ├── tinyply21_test.cpp ├── tinyply22_test.cpp ├── tinyply23_test.cpp └── turkply_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | .DS_Store 3 | bin 4 | bin/** 5 | tmp_bin 6 | tmp_bin/** 7 | results 8 | results/** 9 | *.sh 10 | .vscode 11 | .vscode/** 12 | benchmark/__pycache__ 13 | benchmark/__pycache__/** 14 | benchmark/table.md 15 | test.ply 16 | **.ply 17 | **.txt 18 | **.obj 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ply File I/O Benchmark 2 | 3 | This repository contains code for comparison of various libraries for input and output of PLY files. [PLY file format](https://en.wikipedia.org/wiki/PLY_(file_format)) has been developed at Stanford University by [Greg Turk](https://www.cc.gatech.edu/~turk/) as a part of the [real-world object digitization project](http://graphics.stanford.edu/data/3Dscanrep/) undertaken by Stanford University in mid-90s. 4 | 5 | ## Task 6 | The task is to read and write a basic triangle mesh stored in a PLY. The triangle mesh is represented as a list of vertices and a list of triangles, indicating which triplets of vertices make a triangle. 7 | 8 | The data structure we wish to populate is: 9 | ~~~~ 10 | typedef struct vec3f 11 | { 12 | float x,y,z; 13 | } Vec3f; 14 | 15 | typedef struct tri 16 | { 17 | int index1, index2, index3; 18 | } Tri; 19 | 20 | typedef struct triangle_mesh 21 | { 22 | int n_verts; 23 | int n_faces; 24 | Vec3f* vertices; 25 | Tri* faces; 26 | } TriMesh; 27 | ~~~~ 28 | 29 | The meshes we are testing were processed to contain only the position attribute and only triangle faces. As such this task aims to measure the speed at which each library is able to exchange simple triangular mesh data. Each of the meshes is tested using both binary and text(ASCII) formats, as supported by PLY file format. For binary we use little-endian. 30 | 31 | ### Mesh size vs. number of meshes 32 | 33 | This benchmark focuses on rather large meshes (15k - 28 million triangles). The use case this benchmark analyzes is to minimize the time taken to load such large meshes. If your task is to read a lot of smaller .ply files, then this benchmark might not be reflective of your situation. 34 | 35 | For an alternative task, where a large number of smaller meshes is parsed, and where meshes might have more varied per-vertex attribute list, please see the excellent [ply-parsing-perf](https://github.com/vilya/ply-parsing-perf) benchmark by Vilya Harvey. 36 | 37 | ### Known list size 38 | Given that in our task we process only triangular meshes, it would be good to let the application know this information. 39 | Some libraries (see below) allow passing the expected size of list properties, leading to non-negligible speed-up in parsing. As such, where applicable, this feature has been enabled. 40 | 41 | ### Test Models 42 | 43 | The table below lists models used for this benchmark, along with the source. 44 | 45 | | Model Name | N. Vertices | N. Tris | Source | 46 | |:-----------------------------:|:-----------:|:--------:|:------:| 47 | | suzanne | 7958 | 15744 | Blender 48 | | scannet_scene0402_00 | 93834 | 177518 | [Scannet](http://www.scan-net.org/) 49 | | angel | 237018 | 474048 | [Large Geometric Models Archvive](https://www.cc.gatech.edu/projects/large_models/) 50 | | blade | 882954 | 1765388 | [Large Geometric Models Archvive](https://www.cc.gatech.edu/projects/large_models/) 51 | | hand | 327323 | 654666 | [Large Geometric Models Archvive](https://www.cc.gatech.edu/projects/large_models/) 52 | | horse | 48485 | 96966 | [Large Geometric Models Archvive](https://www.cc.gatech.edu/projects/large_models/) 53 | | armadillo | 172974 | 345944 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 54 | | bunny | 35947 | 69451 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 55 | | dragon | 437645 | 871414 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 56 | | happy_buddha | 543652 | 1087716 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 57 | | lucy | 14027872 | 28055742 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 58 | | xyzrgb_dragon | 3609600 | 7219045 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 59 | | xyzrgb_statuette | 4999996 | 10000000 | [Stanford 3D Scaning Repository](http://graphics.stanford.edu/data/3Dscanrep/) 60 | | bust_of_sappho | 140864 | 281724 | [Thingiverse](https://www.thingiverse.com/thing:14565) 61 | | statue | 999517 | 1999038 | [Sketchfab](https://sketchfab.com/3d-models/william-huskisson-statue-ee9ce7c99f2d4b40aa46aaffb02bf21d) 62 | | speeder_bike | 1473341 | 2947046 | [Sketchfab](https://sketchfab.com/3d-models/speeder-bike-from-star-wars-galaxys-edge-dcddea22a0674737b4201a025a27a94d) 63 | | armchair | 11558 | 23102 | [Sketchfab](https://sketchfab.com/3d-models/lounger-armchair-e9d9d87c32f144e2873765e66814f727) 64 | | bust_of_angelique_dhannetaire | 250000 | 500000 | [Sketchfab](https://sketchfab.com/3d-models/bust-of-angelique-dhannetaire-26c23265310a4e45aaa296d02db83cb2) 65 | 66 | ## Libraries 67 | 68 | Below is a list of libraries used in this benchmark: 69 | 70 | | Library | Author| Language | Known list size | Notes | 71 | |--------:|------------:|---------:|-------------------:|-:| 72 | | [turkply](https://people.sc.fsu.edu/~jburkardt/c_src/ply_io/ply_io.html) | [Greg Turk](https://www.cc.gatech.edu/~turk/) | c |:x: | Original PLY library [link](https://www.cc.gatech.edu/projects/large_models/ply.html) 73 | | [rply](http://w3.impa.br/~diego/software/rply/) | [Diego Nehab](http://w3.impa.br/~diego/index.html) | c | :x: | 74 | | [msh_ply](https://github.com/mhalber/msh) | [Maciej Halber](https://github.com/mhalber) | c | :heavy_check_mark: | 75 | | [happly](https://github.com/nmwsharp/happly) | [Nicolas Sharp](https://github.com/nmwsharp) | c++ | :x: | 76 | | [miniply](https://github.com/vilya/miniply) | [Vilya Harvey](https://github.com/vilya) | c++ | :heavy_check_mark: | Only supports reading PLY files| 77 | | [micro_ply](https://github.com/maluoi/header-libs) | [Nick Klingensmith](https://github.com/maluoi) | c++ | :x: | Only supports reading ASCII PLY files | 78 | | [nanoply](https://github.com/cnr-isti-vclab/vcglib/tree/master/wrap/nanoply) | [vcglib](https://github.com/cnr-isti-vclab/vcglib) | c++ | :x: | 79 | | [plylib](https://github.com/cnr-isti-vclab/vcglib/tree/master/wrap/ply) | [vcglib](https://github.com/cnr-isti-vclab/vcglib) | c++ | :x: | PLY reading/writing used by Meshlab(?) 80 | | [tinyply](https://github.com/ddiakopoulos/tinyply) | [Dimitri Diakopoulos](https://github.com/ddiakopoulos) | c++ | :heavy_check_mark: | This benchmark includes versions 2.1, 2.2 and 2.3 of this library. 81 | 82 | For the usage examples, as well as some additional comments about each of the libraries please check the tests/*_test.c(pp) files. 83 | 84 | ## Results 85 | 86 | Below we present results for parsing PLY files storing data in both ASCII and binary format (little-endian). Times are given in milliseconds. Highlighted numbers indicate the best method in each category. 87 | As noted before, where applicable, a known list size is passed to the library. 88 | 89 | The benchmark was compiled using MSVC 19.28.29334 with \O2 optimization flag, using AMD Ryzen 3900XT and Samsung 970 EVO PLUS. 90 | 91 | To run the test, we run a separate program for each file that attempts to read and write the input file, and reports time taken to do so. Program for each library is run 10 times and the results are averaged. 92 | The averaged time taken for each model is used to compute the overall average time it took to process all the models. 93 | 94 | * Disclaimer: I am the author of msh_ply library. If you see any deficiencies in code for other libraries, don't hesitate to let me know - I hope to make this benchmark as fair as possible. * 95 | 96 | ### Average Read Times 97 | 98 | |Method | ASCII | Binary | 99 | -----------:|------------------:|-------------------:| 100 | |happly | 19104.671(75.3x) | 589.435(16.4x) | 101 | |micro_ply | 1131.752(4.5x) | N/A | 102 | |miniply | **253.671(1.0x)** | **35.935(1.0x)** | 103 | |msh_ply | 2009.957(7.9x) | 40.885(1.1x) | 104 | |nanoply | 8003.712(31.6x) | 106.312(3.0x) | 105 | |plylib | 3157.350(12.4x) | 338.514(9.4x) | 106 | |rply | 1731.580(6.8x) | 327.164(9.1x) | 107 | |tinyply21 | 11583.986(45.7x) | 1844.445(51.3x) | 108 | |tinyply22 | 7561.799(29.8x) | 318.069(8.9x) | 109 | |tinyply23 | 7500.844(29.6x) | 294.265(8.2x) | 110 | |turkply | 2086.552(8.2x) | 549.367(15.3x)| 111 | 112 | 113 | ### Average Write Times 114 | 115 | |Method | ASCII | Binary | 116 | |-----------:|-------------------:|-------------------:| 117 | |happly | 11534.080(3.8x) | 1454.963(19.8x) | 118 | |msh_ply | 4178.405(1.4x) | **73.406(1.0x)** | 119 | |nanoply | 8772.179(2.9x) | 107.735(1.5x) | 120 | |plylib | **3045.147(1.0x)** | 315.647(4.3x) | 121 | |rply | 3966.512(1.3x) | 261.940(3.6x) | 122 | |tinyply21 | 9667.221(3.2x) | 1449.753(19.7x) | 123 | |tinyply22 | 9870.520(3.2x) | 526.407(7.2x) | 124 | |tinyply23 | 9653.622(3.2x) | 560.677(7.6x) | 125 | |turkply | 4017.640(1.3x) | 624.668(8.5x) | 126 | 127 | **Notes**: 128 | - miniply is the fastest library for reading the ply files. If you're only interested in reading files and use C++, it is a great choice. 129 | - miniply and micro_ply do not support the writing of ply files. 130 | - micro_ply does not support binary files, only ASCII format. 131 | - In C, when you need decent read and write performance, msh_ply is a good choice ;). However, it's ASCII mode requires work, so if your models are mostly stored in ASCII, you might want to use other libraries. 132 | - Some libraries were modified to include getter to establish whether input is binary or ASCII. 133 | - In ASCII mode, happly is unable to convert between __uint__ and __int__. Since some models (angel, bust_of_sappho, bust_of_angelique_dhannetaire ) contain vertex list specified as __uint__, while others use __int__, happly fails to parse the two aforementioned models and would need to be recompiled to support the specific type. Here, we simply omit these models when benchmarking happly. 134 | 135 | ### Per model I/O times: 136 | 137 | | | | | | | 138 | |-:|--|--|--|--| 139 | | ASCII | [Read Times Table](assets/ascii_read_full_table.md) | [Read Times Image](assets/ascii_read_fig.png) | [Write Times Table](assets/ascii_write_full_table.md) | [Write Times Image](assets/ascii_write_fig.png) | 140 | | Binary | [Read Times Table](assets/binary_read_full_table.md) | [Read Times Image](assets/binary_read_fig.png) | [Write Times Table](assets/binary_write_full_table.md) | [Write Times Image](assets/binary_write_fig.png) | 141 | 142 | Note that the images show the read time on a log scale, since the performance of different libraries is significantly different. 143 | 144 | ## LOC 145 | 146 | Another metric we can use for deciding a library is the ease of use. Why LOC is by no means a perfect metric to measure ease of use, it does reflect how much code one needs to 147 | type to get basic PLY I/O done. Also, note that these numbers report only simple versions of reading function without any error reporting, etc. 148 | 149 | | Library | Read LOC | Write LOC | 150 | |:---------:|:-----------:|:---------:| 151 | | miniply | 35 | N/A | 152 | | micro_ply | 25 | N/A | 153 | | msh_ply | 29 | 23 | 154 | | nanoply | 23 | 29 | 155 | | plylib | 78 | 65 | 156 | | rply | 69 | 23 | 157 | | happly | **17** | 26 | 158 | | tinyply | **17** | **10** | 159 | | turkply | 52 | 39 | 160 | 161 | [Large Geometric Models Archvive]: https://www.cc.gatech.edu/projects/large_models/index.html 162 | [Stanford 3D Scaning Repository]: http://graphics.stanford.edu/data/3Dscanrep/ 163 | -------------------------------------------------------------------------------- /assets/archive/ascii_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/archive/ascii_read.png -------------------------------------------------------------------------------- /assets/archive/ascii_read_times.md: -------------------------------------------------------------------------------- 1 | # PLY Read Times - ASCII 2 | 3 | - Times are averages of reading each file 10 times 4 | - Times are in milliseconds 5 | 6 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 7 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 8 | |happly | 3431.149 | 2499.732 | 165.859 | 12608.041 | 729.305 | 0.000 | 0.000 | 6301.265 | 4706.780 | 7878.966 | 694.415 |202183.194 | 1486.653 | 21246.625 | 14361.663 | 115.525 | 52018.832 | 72153.874 | 9 | |miniply | 47.999 | 34.296 | 2.178 | 159.980 | 8.485 | 60.269 | 33.802 | 83.613 | 60.476 | 105.544 | 9.157 | 2721.256 | 22.325 | 279.966 | 186.180 | 1.479 | 686.574 | 960.085 | 10 | |mshply | 331.125 | 233.376 | 15.094 | 1011.122 | 63.942 | 526.641 | 284.949 | 589.515 | 431.395 | 740.941 | 63.926 | 19767.979 | 145.471 | 2052.671 | 1375.866 | 10.227 | 5062.585 | 7020.106 | 11 | |nanoply | 2105.669 | 1527.526 | 102.551 | 7769.915 | 513.940 | 4122.876 | 2322.852 | 3894.996 | 2886.395 | 4835.806 | 430.171 |125387.028 | 872.583 | 13120.012 | 8892.534 | 70.877 | 32211.312 | 44626.962 | 12 | |plylib | 1328.022 | 947.182 | 61.847 | 4781.821 | 247.244 | 2034.220 | 1120.529 | 2491.046 | 1806.988 | 3118.155 | 267.885 | 83172.398 | 680.574 | 8548.487 | 5759.985 | 42.633 | 21074.210 | 29342.138 | 13 | |rply | 607.184 | 442.074 | 29.014 | 1467.243 | 131.752 | 1079.086 | 588.992 | 1117.462 | 820.070 | 1393.450 | 124.920 | 36125.075 | 275.112 | 3823.681 | 2549.278 | 20.044 | 9306.795 | 12891.283 | 14 | |tinyply21 | 2412.470 | 1750.813 | 117.704 | 8883.794 | 383.888 | 2699.724 | 1516.576 | 4468.425 | 3318.366 | 5563.937 | 495.006 |143917.403 | 1001.905 | 15050.250 | 10170.014 | 81.712 | 36968.902 | 51179.896 | 15 | |tinyply22 | 2081.456 | 1508.453 | 102.013 | 7684.622 | 322.524 | 2255.303 | 1270.066 | 3846.304 | 2850.914 | 4794.365 | 428.625 |123766.027 | 842.389 | 12997.625 | 8765.307 | 70.640 | 31793.190 | 44121.511 | 16 | |tinyply23 | 2076.709 | 1515.012 | 101.882 | 7667.246 | 321.061 | 2249.276 | 1264.357 | 3840.085 | 2849.097 | 4769.129 | 427.895 |123775.523 | 840.167 | 12943.769 | 8758.071 | 71.080 | 31718.409 | 44024.928 | 17 | |turkply | 366.503 | 260.344 | 17.277 | 1145.337 | 69.122 | 559.867 | 304.678 | 650.791 | 484.923 | 818.286 | 71.477 | 21656.045 | 159.893 | 2261.868 | 1527.069 | 12.346 | 5568.969 | 7714.543 | -------------------------------------------------------------------------------- /assets/archive/ascii_write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/archive/ascii_write.png -------------------------------------------------------------------------------- /assets/archive/ascii_write_times.md: -------------------------------------------------------------------------------- 1 | # PLY Write Times - ASCII 2 | 3 | - Times are averages of writing each file 10 times 4 | - Times are in milliseconds 5 | - miniply does not support writing files 6 | - happly fails to parse two models 7 | 8 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | 9 | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 10 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 11 | |happly | 948.554 | 740.902 | 53.386 | 3150.032 | 160.768 | failed | failed | 1951.325 | 1248.791 | 2448.391 | 218.609 | 63053.944 | 375.131 | 5794.711 | 3868.328 | 38.831 | 15589.181 | 22049.563 | 12 | |mshply | 565.939 | 414.720 | 31.977 | 2140.139 | 85.269 | 581.564 | 317.513 | 1009.537 | 739.619 | 1254.367 | 116.286 | 36747.760 | 218.017 | 3439.886 | 2329.649 | 21.296 | 8872.743 | 12499.473 | 13 | |nanoply | 735.065 | 572.881 | 43.197 | 2450.509 | 126.766 | 738.334 | 436.583 | 1515.862 | 963.301 | 1883.821 | 169.729 | 49115.768 | 292.161 | 4469.383 | 2997.360 | 33.308 | 12094.325 | 17044.386 | 14 | |plylib | 620.506 | 488.230 | 35.594 | 2072.200 | 109.066 | 519.553 | 313.007 | 1338.772 | 808.892 | 1671.237 | 144.897 | 43905.852 | 241.485 | 3911.360 | 2584.238 | 27.773 | 10681.443 | 15204.737 | 15 | |rply | 582.985 | 423.744 | 32.494 | 2127.690 | 89.766 | 607.955 | 331.461 | 1062.868 | 766.065 | 1323.492 | 122.393 | 37205.209 | 226.065 | 3554.937 | 2417.855 | 24.482 | 9070.455 | 12767.741 | 16 | |tinyply21 | 830.215 | 640.527 | 47.181 | 2795.212 | 140.820 | 846.298 | 492.513 | 1696.170 | 1090.799 | 2109.599 | 191.063 | 54492.058 | 327.796 | 5046.319 | 3395.211 | 34.722 | 13575.132 | 19075.366 | 17 | |tinyply22 | 831.523 | 643.029 | 47.873 | 2794.987 | 142.596 | 848.220 | 493.504 | 1694.679 | 1093.157 | 2111.516 | 189.763 | 54688.111 | 330.904 | 5067.792 | 3399.590 | 34.823 | 13575.816 | 19180.270 | 18 | |tinyply23 | 768.675 | 596.863 | 44.200 | 2555.020 | 133.147 | 774.182 | 455.132 | 1579.156 | 1006.520 | 1964.500 | 176.490 | 50849.971 | 304.511 | 4666.319 | 3125.605 | 32.460 | 12594.339 | 17771.987 | 19 | |turkply | 574.107 | 415.472 | 32.085 | 2079.506 | 89.704 | 594.949 | 322.893 | 1041.481 | 752.239 | 1295.849 | 120.479 | 36462.779 | 222.711 | 3470.812 | 2357.926 | 22.894 | 8900.852 | 12542.474 | -------------------------------------------------------------------------------- /assets/archive/little_endian_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/archive/little_endian_read.png -------------------------------------------------------------------------------- /assets/archive/little_endian_read_times.md: -------------------------------------------------------------------------------- 1 | # PLY Read Times - Binary (Little Endian) 2 | 3 | - Times are averages of reading each file 10 times 4 | - Times are in milliseconds 5 | 6 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 7 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 8 | |happly | 347.398 | 257.242 | 16.699 | 1347.845 | 56.032 | 371.881 | 215.649 | 652.759 | 494.081 | 832.895 | 71.642 | 20885.013 | 151.315 | 2214.270 | 1482.020 | 12.013 | 5306.153 | 7522.343 | 9 | |miniply | 11.792 | 8.145 | 1.018 | 40.426 | 2.387 | 11.636 | 7.989 | 20.226 | 15.133 | 24.942 | 2.478 | 644.786 | 5.187 | 67.546 | 45.295 | 0.468 | 166.219 | 229.099 | 10 | |mshply | 8.212 | 5.950 | 0.492 | 30.826 | 1.652 | 8.617 | 4.892 | 15.424 | 11.479 | 18.930 | 1.606 | 500.778 | 4.436 | 52.445 | 34.793 | 0.377 | 129.301 | 179.907 | 11 | |nanoply | 17.449 | 13.131 | 3.482 | 55.479 | 5.110 | 17.523 | 11.231 | 28.109 | 21.859 | 34.295 | 5.524 | 816.758 | 8.683 | 88.129 | 60.873 | 3.565 | 211.818 | 292.738 | 12 | |plylib | 97.625 | 70.729 | 4.768 | 361.210 | 20.424 | 101.995 | 58.090 | 178.296 | 133.662 | 221.967 | 19.733 | 5730.305 | 50.361 | 599.294 | 406.776 | 3.283 | 1475.177 | 2041.774 | 13 | |rply | 45.959 | 33.469 | 2.277 | 169.934 | 8.927 | 48.139 | 27.291 | 85.272 | 62.928 | 104.736 | 9.254 | 2717.272 | 20.216 | 284.013 | 194.220 | 1.603 | 694.673 | 964.411 | 14 | |tinyply21 | 280.212 | 203.001 | 13.832 | 1045.137 | 59.173 | 292.785 | 165.412 | 511.198 | 382.862 | 639.789 | 57.622 | 16459.342 | 140.503 | 1741.593 | 1171.045 | 9.548 | 4247.607 | 5893.127 | 15 | |tinyply22 | 66.241 | 47.336 | 3.231 | 240.069 | 13.204 | 68.351 | 38.897 | 119.961 | 89.120 | 148.397 | 13.079 | 3842.527 | 31.792 | 401.300 | 273.156 | 2.303 | 989.082 | 1369.369 | 16 | |tinyply23 | 65.521 | 48.305 | 3.247 | 240.822 | 13.243 | 68.341 | 38.593 | 118.846 | 89.169 | 148.639 | 13.188 | 3845.606 | 31.416 | 402.126 | 274.386 | 2.317 | 988.232 | 1370.379 | 17 | |turkply | 132.134 | 93.231 | 6.211 | 490.601 | 26.434 | 138.901 | 74.512 | 229.771 | 181.151 | 286.997 | 26.814 | 7790.054 | 61.092 | 779.810 | 527.949 | 4.325 | 1916.815 | 2646.706 | -------------------------------------------------------------------------------- /assets/archive/little_endian_write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/archive/little_endian_write.png -------------------------------------------------------------------------------- /assets/archive/little_endian_write_times.md: -------------------------------------------------------------------------------- 1 | # PLY Write Times - Binary (Little Endian) 2 | 3 | - Times are averages of writing each file 10 times 4 | - Times are in milliseconds 5 | - miniply does not support writing files 6 | 7 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 8 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 9 | |happly | 287.102 | 213.959 | 17.934 | 1096.122 | 47.501 | 305.634 | 190.602 | 536.387 | 407.856 | 683.065 | 62.227 | 17595.552 | 125.208 | 1806.987 | 1206.434 | 13.638 | 4343.818 | 6171.090 | 10 | |mshply | 16.166 | 11.529 | 4.862 | 42.813 | 6.979 | 14.841 | 23.007 | 25.789 | 18.399 | 27.984 | 6.370 | 648.843 | 13.889 | 72.522 | 47.957 | 5.007 | 166.668 | 232.361 | 11 | |nanoply | 25.476 | 18.480 | 5.463 | 61.925 | 8.754 | 25.012 | 28.794 | 36.790 | 27.352 | 43.844 | 8.490 | 938.203 | 16.607 | 98.729 | 75.988 | 5.894 | 247.090 | 334.850 | 12 | |plylib | 63.198 | 45.290 | 7.151 | 213.240 | 13.893 | 63.496 | 51.216 | 108.952 | 82.126 | 132.893 | 15.715 | 3374.296 | 31.007 | 353.825 | 241.176 | 6.491 | 865.965 | 1203.212 | 13 | |rply | 42.277 | 32.884 | 6.871 | 139.828 | 10.965 | 40.113 | 38.843 | 78.766 | 53.952 | 87.982 | 11.732 | 2179.693 | 22.788 | 232.469 | 172.235 | 5.851 | 560.893 | 784.632 | 14 | |tinyply21 | 191.704 | 138.339 | 13.900 | 700.484 | 32.933 | 197.945 | 125.986 | 344.139 | 259.044 | 428.080 | 42.911 | 10939.388 | 79.210 | 1154.910 | 782.939 | 10.946 | 2817.281 | 3914.888 | 15 | |tinyply22 | 108.565 | 78.770 | 9.280 | 381.109 | 20.819 | 112.160 | 78.578 | 193.217 | 143.820 | 236.803 | 25.369 | 6068.827 | 47.909 | 636.669 | 432.279 | 8.122 | 1549.880 | 2160.857 | 16 | |tinyply23 | 110.746 | 79.066 | 9.322 | 386.728 | 20.989 | 113.038 | 77.651 | 193.265 | 145.404 | 238.538 | 25.601 | 6108.742 | 47.543 | 636.032 | 435.159 | 8.064 | 1561.187 | 2175.912 | 17 | |turkply | 111.753 | 78.658 | 9.053 | 397.951 | 21.171 | 114.337 | 77.201 | 187.973 | 148.980 | 237.752 | 25.937 | 6283.814 | 47.496 | 630.014 | 421.374 | 7.981 | 1551.981 | 2139.007 | -------------------------------------------------------------------------------- /assets/ascii_read_avg_table.md: -------------------------------------------------------------------------------- 1 | |Method |Time(ms) | 2 | ----------:|----------:| 3 | |happly | 19104.671(75.3x)| 4 | |microply | 1131.752(4.5x)| 5 | |miniply | * 253.671(1.0x)*| 6 | |mshply | 2009.957(7.9x)| 7 | |nanoply | 8003.712(31.6x)| 8 | |plylib | 3157.350(12.4x)| 9 | |rply | 1731.580(6.8x)| 10 | |tinyply21 | 11583.986(45.7x)| 11 | |tinyply22 | 7561.799(29.8x)| 12 | |tinyply23 | 7500.844(29.6x)| 13 | |turkply | 2086.552(8.2x)| 14 | -------------------------------------------------------------------------------- /assets/ascii_read_fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/ascii_read_fig.png -------------------------------------------------------------------------------- /assets/ascii_read_full_table.md: -------------------------------------------------------------------------------- 1 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 2 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 3 | |happly | 0.000 | 1784.472 | 118.915 | 8670.360 | 439.915 | 0.000 | 0.000 | 4481.201 | 3331.531 | 5592.547 | 496.337 |145947.836 | 1223.984 | 15226.472 | 10386.426 | 80.341 | 37172.141 | 51617.590 | 4 | |microply | 170.248 | 121.155 | 8.017 | 501.468 | 33.457 | 276.342 | 151.115 | 306.123 | 225.599 | 381.618 | 34.119 | 10114.534 | 75.482 | 1051.357 | 711.489 | 5.437 | 2601.632 | 3602.354 | 5 | |miniply | 40.852 | 30.280 | 2.332 | 134.499 | 7.739 | 52.000 | 29.110 | 71.209 | 51.533 | 88.732 | 8.530 | 2253.975 | 19.298 | 238.858 | 159.580 | 2.030 | 577.165 | 798.351 | 6 | |mshply | 293.840 | 206.977 | 13.192 | 904.990 | 55.663 | 462.922 | 248.969 | 542.321 | 388.762 | 678.744 | 58.659 | 18034.860 | 127.533 | 1851.897 | 1239.134 | 9.055 | 4621.878 | 6439.838 | 7 | |nanoply | 1214.030 | 863.302 | 55.382 | 4102.847 | 221.426 | 1719.558 | 946.103 | 2164.268 | 1590.781 | 2698.421 | 237.606 | 71752.611 | 580.219 | 7434.243 | 5079.409 | 37.881 | 18189.628 | 25179.098 | 8 | |plylib | 476.120 | 345.294 | 23.636 | 1624.023 | 91.548 | 775.692 | 432.038 | 888.196 | 640.481 | 1100.926 | 100.276 | 27913.594 | 232.122 | 2944.850 | 1984.270 | 17.662 | 7233.144 | 10008.429 | 9 | |rply | 255.641 | 183.188 | 11.662 | 810.350 | 48.565 | 396.401 | 218.622 | 462.969 | 343.504 | 577.013 | 50.672 | 15505.927 | 116.709 | 1604.587 | 1076.422 | 7.905 | 3985.982 | 5512.328 | 10 | |tinyply21 | 1752.946 | 1258.073 | 81.694 | 6087.122 | 287.321 | 2077.615 | 1157.690 | 3167.115 | 2343.569 | 3939.687 | 350.333 |103656.878 | 777.874 | 10823.005 | 7417.755 | 55.396 | 26523.707 | 36753.962 | 11 | |tinyply22 | 1149.408 | 824.110 | 52.987 | 3877.018 | 177.867 | 1307.636 | 723.416 | 2055.345 | 1515.408 | 2562.497 | 226.982 | 67773.530 | 474.789 | 7077.513 | 4839.779 | 36.076 | 17364.324 | 24073.700 | 12 | |tinyply23 | 1134.152 | 811.953 | 52.710 | 3824.634 | 176.151 | 1289.369 | 715.222 | 2021.834 | 1491.002 | 2523.636 | 223.752 | 67416.315 | 468.571 | 6996.360 | 4776.635 | 35.431 | 17187.982 | 23869.487 | 13 | |turkply | 308.290 | 223.986 | 14.545 | 988.635 | 56.336 | 462.039 | 249.270 | 571.017 | 420.858 | 707.014 | 61.849 | 18694.304 | 132.315 | 1933.317 | 1298.440 | 9.732 | 4770.202 | 6655.787 | 14 | -------------------------------------------------------------------------------- /assets/ascii_write_avg_table.md: -------------------------------------------------------------------------------- 1 | |Method |Time(ms) | 2 | ----------:|----------:| 3 | |happly | 11534.080(3.8x)| 4 | |mshply | 4178.405(1.4x)| 5 | |nanoply | 8772.179(2.9x)| 6 | |plylib | * 3045.147(1.0x)*| 7 | |rply | 3966.512(1.3x)| 8 | |tinyply21 | 9667.221(3.2x)| 9 | |tinyply22 | 9870.520(3.2x)| 10 | |tinyply23 | 9653.622(3.2x)| 11 | |turkply | 4017.640(1.3x)| 12 | -------------------------------------------------------------------------------- /assets/ascii_write_fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/ascii_write_fig.png -------------------------------------------------------------------------------- /assets/ascii_write_full_table.md: -------------------------------------------------------------------------------- 1 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 2 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 3 | |happly | 0.000 | 1082.385 | 75.054 | 5394.654 | 223.139 | 0.000 | 0.000 | 2685.578 | 2024.498 | 3377.741 | 303.442 | 87990.449 | 575.365 | 9162.074 | 6215.075 | 52.436 | 22539.121 | 31310.185 | 4 | |mshply | 636.233 | 460.677 | 32.822 | 2337.234 | 95.821 | 654.982 | 369.309 | 1153.393 | 850.805 | 1433.740 | 128.633 | 37485.582 | 244.925 | 3867.637 | 2619.168 | 23.425 | 9525.258 | 13291.650 | 5 | |nanoply | 1348.396 | 975.310 | 67.456 | 4897.357 | 203.195 | 1402.495 | 792.357 | 2486.349 | 1833.170 | 3080.349 | 279.858 | 78423.314 | 519.292 | 8223.574 | 5567.840 | 49.517 | 20037.909 | 27711.484 | 6 | |plylib | 457.922 | 333.572 | 24.561 | 1638.777 | 72.036 | 505.162 | 286.573 | 859.028 | 619.882 | 1071.872 | 96.432 | 27312.697 | 184.327 | 2841.028 | 1905.589 | 18.848 | 6941.210 | 9643.137 | 7 | |rply | 609.781 | 442.681 | 31.864 | 2192.624 | 94.710 | 632.235 | 360.014 | 1139.574 | 827.868 | 1421.977 | 128.893 | 35318.924 | 236.477 | 3751.085 | 2524.026 | 25.556 | 9106.262 | 12552.671 | 8 | |tinyply21 | 1474.259 | 1068.016 | 73.284 | 5393.185 | 221.580 | 1540.420 | 870.206 | 2742.333 | 2016.925 | 3407.911 | 304.475 | 86208.005 | 567.604 | 9085.286 | 6134.986 | 52.581 | 22156.857 | 30692.067 | 9 | |tinyply22 | 1506.848 | 1098.698 | 75.687 | 5488.472 | 227.880 | 1584.562 | 892.746 | 2801.347 | 2063.148 | 3476.269 | 311.379 | 87895.490 | 578.697 | 9282.130 | 6267.719 | 53.041 | 22693.798 | 31371.449 | 10 | |tinyply23 | 1471.047 | 1071.363 | 73.473 | 5353.151 | 223.153 | 1539.726 | 870.684 | 2730.562 | 2013.990 | 3395.636 | 307.031 | 86083.048 | 568.115 | 9036.070 | 6136.620 | 53.124 | 22141.241 | 30697.163 | 11 | |turkply | 614.742 | 448.412 | 32.288 | 2225.506 | 95.255 | 638.350 | 363.876 | 1149.471 | 842.986 | 1426.148 | 129.430 | 35821.573 | 241.227 | 3771.397 | 2551.556 | 24.535 | 9198.683 | 12742.084 | 12 | -------------------------------------------------------------------------------- /assets/binary_read_avg_table.md: -------------------------------------------------------------------------------- 1 | |Method |Time(ms) | 2 | ----------:|----------:| 3 | |happly | 589.435(16.4x)| 4 | |miniply | * 35.935(1.0x)*| 5 | |mshply | 40.885(1.1x)| 6 | |nanoply | 106.312(3.0x)| 7 | |plylib | 338.514(9.4x)| 8 | |rply | 327.164(9.1x)| 9 | |tinyply21 | 1844.445(51.3x)| 10 | |tinyply22 | 318.069(8.9x)| 11 | |tinyply23 | 294.265(8.2x)| 12 | |turkply | 549.367(15.3x)| 13 | -------------------------------------------------------------------------------- /assets/binary_read_fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/binary_read_fig.png -------------------------------------------------------------------------------- /assets/binary_read_full_table.md: -------------------------------------------------------------------------------- 1 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 2 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 3 | |happly | 88.445 | 64.274 | 4.258 | 325.557 | 19.079 | 93.516 | 54.386 | 160.457 | 121.754 | 199.912 | 17.648 | 5262.958 | 44.660 | 550.460 | 370.124 | 3.571 | 1353.474 | 1875.299 | 4 | |miniply | 5.230 | 4.038 | 0.453 | 20.545 | 1.218 | 5.782 | 3.351 | 10.001 | 7.517 | 12.814 | 1.200 | 314.586 | 2.406 | 34.778 | 23.374 | 0.361 | 85.584 | 113.582 | 5 | |mshply | 6.291 | 4.655 | 0.442 | 23.160 | 1.530 | 6.529 | 3.713 | 11.306 | 8.460 | 14.123 | 1.221 | 358.437 | 3.344 | 40.842 | 26.081 | 0.316 | 94.944 | 130.534 | 6 | |nanoply | 17.132 | 12.368 | 0.980 | 59.838 | 3.173 | 17.967 | 10.547 | 30.325 | 23.152 | 37.782 | 3.420 | 943.641 | 7.451 | 99.336 | 67.998 | 0.733 | 242.561 | 335.208 | 7 | |plylib | 50.833 | 37.274 | 2.568 | 191.084 | 12.096 | 54.282 | 30.553 | 92.977 | 69.976 | 116.054 | 10.338 | 3001.837 | 26.881 | 314.834 | 215.549 | 1.789 | 773.190 | 1091.140 | 8 | |rply | 49.650 | 36.663 | 2.477 | 183.797 | 9.671 | 52.231 | 29.577 | 90.467 | 68.424 | 113.416 | 10.006 | 2915.948 | 21.574 | 304.658 | 206.121 | 1.722 | 745.852 | 1046.701 | 9 | |tinyply21 | 277.337 | 202.876 | 13.613 | 1037.394 | 63.447 | 293.176 | 165.735 | 510.236 | 383.634 | 635.345 | 56.351 | 16423.687 | 152.337 | 1724.308 | 1166.416 | 9.289 | 4232.953 | 5851.870 | 10 | |tinyply22 | 48.296 | 35.261 | 2.535 | 182.717 | 12.333 | 50.620 | 28.639 | 87.839 | 66.538 | 110.064 | 9.812 | 2833.718 | 27.499 | 295.202 | 200.226 | 1.752 | 725.668 | 1006.522 | 11 | |tinyply23 | 44.378 | 32.509 | 2.342 | 165.429 | 11.649 | 46.669 | 26.434 | 81.102 | 60.951 | 102.839 | 9.085 | 2615.109 | 25.929 | 274.127 | 187.128 | 1.648 | 674.574 | 934.870 | 12 | |turkply | 82.274 | 60.000 | 4.161 | 307.146 | 17.248 | 86.782 | 48.758 | 152.218 | 114.361 | 190.139 | 16.737 | 4881.280 | 41.446 | 515.975 | 349.348 | 2.862 | 1262.301 | 1755.570 | 13 | -------------------------------------------------------------------------------- /assets/binary_write_avg_table.md: -------------------------------------------------------------------------------- 1 | |Method |Time(ms) | 2 | ----------:|----------:| 3 | |happly | 1454.963(19.8x)| 4 | |mshply | * 73.406(1.0x)*| 5 | |nanoply | 107.735(1.5x)| 6 | |plylib | 315.647(4.3x)| 7 | |rply | 261.940(3.6x)| 8 | |tinyply21 | 1449.753(19.7x)| 9 | |tinyply22 | 526.407(7.2x)| 10 | |tinyply23 | 560.677(7.6x)| 11 | |turkply | 624.668(8.5x)| 12 | -------------------------------------------------------------------------------- /assets/binary_write_fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhalber/ply_io_benchmark/0ee404b37b4c8cb7459615fb1ec248559d8e939a/assets/binary_write_fig.png -------------------------------------------------------------------------------- /assets/binary_write_full_table.md: -------------------------------------------------------------------------------- 1 | | | angel | armadillo | armchair | blade | bunny |bust_of_angelique_dhannetaire | bust_of_sappho | dragon | hand | happy_buddha | horse | lucy | scannet_scene0402_00 | speeder_bike | statue | suzanne | xyzrgb_dragon | xyzrgb_statuette | 2 | |----------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:|-----------------------:| 3 | |happly | 220.528 | 157.640 | 13.329 | 805.793 | 35.839 | 230.896 | 140.035 | 397.211 | 290.141 | 503.060 | 46.380 | 13193.011 | 85.328 | 1345.406 | 903.420 | 11.094 | 3268.281 | 4541.939 | 4 | |mshply | 14.276 | 10.920 | 4.234 | 41.504 | 6.176 | 13.669 | 21.503 | 23.386 | 17.089 | 26.574 | 5.267 | 630.551 | 9.754 | 68.129 | 46.685 | 3.948 | 157.048 | 220.589 | 5 | |nanoply | 24.729 | 17.581 | 4.315 | 61.826 | 7.752 | 23.997 | 27.460 | 36.958 | 26.348 | 43.461 | 7.497 | 903.703 | 14.176 | 101.987 | 72.508 | 4.320 | 234.622 | 325.996 | 6 | |plylib | 50.769 | 36.744 | 5.990 | 178.403 | 11.289 | 51.877 | 43.471 | 92.128 | 66.856 | 109.671 | 13.208 | 2788.711 | 24.394 | 289.656 | 201.366 | 5.378 | 715.110 | 996.618 | 7 | |rply | 45.732 | 31.834 | 5.922 | 145.537 | 16.260 | 48.314 | 39.252 | 75.421 | 55.912 | 93.812 | 11.438 | 2307.140 | 21.904 | 243.069 | 163.749 | 5.186 | 591.606 | 812.830 | 8 | |tinyply21 | 225.086 | 162.373 | 14.647 | 817.570 | 37.383 | 233.447 | 144.652 | 405.959 | 302.321 | 499.618 | 48.835 | 12858.260 | 88.514 | 1363.921 | 925.859 | 11.006 | 3322.588 | 4633.514 | 9 | |tinyply22 | 82.974 | 63.026 | 8.198 | 296.006 | 17.260 | 87.133 | 63.045 | 150.758 | 114.110 | 183.083 | 20.094 | 4665.612 | 36.971 | 488.466 | 330.931 | 6.800 | 1198.054 | 1662.805 | 10 | |tinyply23 | 88.656 | 64.407 | 8.006 | 313.482 | 17.327 | 91.530 | 66.100 | 159.694 | 119.045 | 197.390 | 20.809 | 4977.320 | 38.156 | 518.585 | 355.445 | 6.905 | 1276.036 | 1773.292 | 11 | |turkply | 98.136 | 72.246 | 8.464 | 349.143 | 18.465 | 101.845 | 71.626 | 176.988 | 132.032 | 216.926 | 22.223 | 5540.560 | 42.713 | 585.072 | 396.622 | 7.011 | 1425.870 | 1978.077 | 12 | -------------------------------------------------------------------------------- /benchmark/compute_results.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import argparse 3 | import re 4 | import numpy as np 5 | import math 6 | from matplotlib.font_manager import FontProperties 7 | import matplotlib.pyplot as plt 8 | import matplotlib.patheffects as path_effects 9 | from os import path 10 | from os import listdir 11 | from run_test import run_test 12 | from os.path import isfile, join 13 | 14 | def print_full_table( model_names, method_names, values, filename=None ): 15 | with open(filename, "w") as f: 16 | print( "|%-10s |" % "", end="", file=f ) 17 | for j in range(0, len(model_names)): 18 | print( "%23s |" % model_names[j], end="", file=f ) 19 | print("", file=f ) 20 | print("|", end="", file=f ) 21 | print( "-" * 10, end="", file=f ) 22 | print( ":|", end="", file=f ) 23 | for j in range(0, len(model_names)): 24 | print( "-"*23, end="", file=f ) 25 | print( ":|", end="", file=f ) 26 | print("", file=f ) 27 | 28 | for i in range(0, len(method_names)): 29 | method = method_names[i] 30 | print( "|%-10s |" % method, end="", file=f ) 31 | for j in range(0, len(model_names)): 32 | value = values[i][j] 33 | print("%10.3f |" % (value), end="", file=f ) 34 | print("", file=f ) 35 | 36 | def print_average_table( model_names, method_names, values, filename = None ): 37 | print("TEST") 38 | with open(filename, "w") as f: 39 | print( "|%-10s |%-10s |" % ("Method", "Time(ms)"), file=f) 40 | print( "-" * 10, end="", file=f) 41 | print( ":|", end="", file=f) 42 | print( "-" * 10, end="", file=f) 43 | print( ":|", file=f) 44 | 45 | print("-------------------", len(method_names)) 46 | method_average_times = {} 47 | for i in range(0, len(method_names)): 48 | print(method_names[i], len(method_names), i, len(values)) 49 | method = method_names[i] 50 | cur_values = [v for v in values[i] if v > 0.0] 51 | average = 1000.0 52 | if len(cur_values): 53 | average = sum(cur_values) / len(cur_values) 54 | method_average_times[method] = average 55 | 56 | min_method = min( method_average_times, key=method_average_times.get) 57 | min_time = method_average_times[min_method] 58 | for i in range(0, len(method_names)): 59 | method = method_names[i] 60 | time = method_average_times[method] 61 | if method == min_method: 62 | print("|%-10s | *%10.3f(%2.1fx)*|" % (method, time, time/min_time), file=f) 63 | else: 64 | print("|%-10s | %10.3f(%2.1fx)|" % (method, time, time/min_time), file=f) 65 | 66 | def create_results_figure( model_names, method_names, values, filename ): 67 | results_table = np.array( values ).transpose() 68 | 69 | n_models = results_table.shape[0] 70 | n_methods = results_table.shape[1] 71 | 72 | index = np.arange(n_methods) 73 | cmap = plt.get_cmap('tab20') 74 | x = np.linspace(0.0, 1.0, 100) 75 | 76 | font0 = FontProperties() 77 | font0.set_weight('medium') 78 | font0.set_size('medium') 79 | font1 = font0.copy() 80 | font1.set_size('x-large') 81 | font2 = font0.copy() 82 | font2.set_size('xx-large') 83 | 84 | dpi = 70 85 | step = (1.0 / n_models) * 0.9 86 | xmax = 100000.0 87 | plt.figure(figsize=(1000/dpi, 2500/dpi), dpi=dpi) 88 | for i in range(n_models): 89 | color = cmap(i/n_models) 90 | ax = plt.barh(index-0.35 + i * step, results_table[i], step, 91 | alpha=1.0, 92 | color=color, 93 | label=model_names[i]) 94 | rects = ax.patches 95 | 96 | for j in range(len(rects)): 97 | rect = rects[j] 98 | read_time = results_table[i][j] 99 | if read_time == 0: 100 | label = 'N/A' 101 | else: 102 | label = ' %-7.2f' % (results_table[i][j]) 103 | w = min( rect.get_width(), xmax - 1500 ) 104 | h = rect.get_y() + rect.get_height() * 0.45 105 | if w == 0: 106 | w = 0.32 107 | 108 | plt.ylabel('Library', fontproperties=font1) 109 | plt.xlabel('Time (log ms)', fontproperties=font1) 110 | plt.legend(bbox_to_anchor=(1.1,1.0)) 111 | plt.xscale('log') 112 | plt.xlim(0.3, xmax) 113 | plt.yticks(np.arange(n_methods), method_names) 114 | plt.tight_layout() 115 | # plt.show() 116 | plt.savefig(filename, dpi=dpi) 117 | 118 | def compute_results(results_folder, output_base_name): 119 | results_names = [r for r in listdir( results_folder ) if isfile(join(results_folder, r))] 120 | 121 | # a bit of roundabout way of doing this but its late and I want to be done 122 | # Essentially we need to figure out what is the number of methods and method of models 123 | method_set = set() 124 | model_set = set() 125 | for result_name in results_names: 126 | base_name, ext = result_name.split('.') 127 | if ext!="txt": 128 | continue 129 | tokens = base_name.split('_') 130 | method_name = tokens[0] 131 | model_name = tokens[2] 132 | if( len(tokens) > 3 ): 133 | model_name = '_'.join([tokens[2], tokens[3]]) 134 | if( len(tokens) > 4 ): 135 | model_name = '_'.join([tokens[2], tokens[3], tokens[4]]) 136 | if( len(tokens) > 5 ): 137 | model_name = '_'.join([tokens[2], tokens[3], tokens[4], tokens[5]]) 138 | 139 | method_set.add( method_name ) 140 | model_set.add( model_name ) 141 | 142 | # sort the methods and setup variables to store all results 143 | method_names = sorted(list(method_set)) 144 | model_names = sorted(list(model_set)) 145 | all_read_times = [] 146 | all_write_times = [] 147 | 148 | # read in the result data 149 | for i in range(0, len(method_names)): 150 | method = method_names[i] 151 | read_times = [] 152 | write_times = [] 153 | for j in range(0, len(model_names)): 154 | model = model_names[j] 155 | result_name = ('_').join([method, "test", model]) +".txt" 156 | result_file = open(path.join(results_folder,result_name), 'r') 157 | result_lines = [ x.rstrip() for x in result_file.readlines() if len(x) > 1 ] 158 | if len(result_lines) > 0: 159 | values = [[float(x) for x in v.split(" ")] for v in result_lines] 160 | else: 161 | values = [[0.0, 0.0]] # did not get any results 162 | result_file.close() 163 | vals_sum = [sum(x) for x in zip(*values)] 164 | avg_values = [ x/len(values) for x in vals_sum ] 165 | read_times.append( avg_values[0] ) 166 | write_times.append( avg_values[1] ) 167 | all_read_times.append( read_times ) 168 | if (method != "miniply") and (method != "microply"): 169 | all_write_times.append( write_times ) 170 | 171 | avg_read_table_filename = output_base_name + "_read_avg_table.md" 172 | full_read_table_filename = output_base_name + "_read_full_table.md" 173 | read_fig_filename = output_base_name + "_read_fig.png" 174 | 175 | print_average_table( model_names, method_names, all_read_times, avg_read_table_filename ) 176 | print_full_table( model_names, method_names, all_read_times, full_read_table_filename ) 177 | create_results_figure( model_names, method_names, all_read_times, read_fig_filename ) 178 | 179 | method_names.remove("miniply") 180 | method_names.remove("microply") 181 | 182 | 183 | avg_write_table_filename = output_base_name + "_write_avg_table.md" 184 | full_write_table_filename = output_base_name + "_write_full_table.md" 185 | write_fig_filename = output_base_name + "_write_fig.png" 186 | 187 | print_average_table( model_names, method_names, all_write_times, avg_write_table_filename ) 188 | print_full_table( model_names, method_names, all_write_times, full_write_table_filename ) 189 | create_results_figure( model_names, method_names, all_write_times, write_fig_filename ) 190 | 191 | def parse_arguments(): 192 | parser = argparse.ArgumentParser(description='Run benchmarks') 193 | parser.add_argument('results_folder', help='Folder where to store result file', default="./results/") 194 | parser.add_argument('output_base_name', help='Base name of the result files', default=None ) 195 | 196 | return parser.parse_args() 197 | 198 | if __name__ == "__main__": 199 | args = parse_arguments() 200 | compute_results( args.results_folder, args.output_base_name ) -------------------------------------------------------------------------------- /benchmark/run_benchmark.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import argparse 3 | from os import path 4 | from os import listdir 5 | from run_test import run_test 6 | from os.path import isfile, join 7 | 8 | 9 | def run_benchmark(args): 10 | models_names = [path.join(args.model_folder, f) for f in listdir( args.model_folder ) if isfile(join(args.model_folder, f)) ] 11 | binaries_names = [path.join(args.binary_folder, b) for b in listdir( args.binary_folder ) if isfile(join(args.binary_folder, b)) and path.splitext(b)[1] == ".exe" ] 12 | 13 | n_tests = len(binaries_names) * len(models_names) 14 | cur_test = 0 15 | for binary_name in binaries_names: 16 | for model_name in models_names: 17 | print( "%4d/%4d Testing %s with model %s" % (cur_test, n_tests, binary_name, model_name) ) 18 | run_test( binary_name, model_name, args.n_tries, args.results_folder ) 19 | cur_test = cur_test+1 20 | 21 | 22 | def parse_arguments(): 23 | parser = argparse.ArgumentParser(description='Run benchmarks') 24 | parser.add_argument('model_folder', help='Folder where all models are stored') 25 | parser.add_argument('binary_folder', help="Folder where all binaries are stored") 26 | parser.add_argument('--n_tries', type=int, help="Number of tries we will run for each test", default=10) 27 | parser.add_argument('--results_folder', help='Folder where to store result file', default="./results/") 28 | 29 | return parser.parse_args() 30 | 31 | if __name__ == "__main__": 32 | args = parse_arguments() 33 | run_benchmark(args) -------------------------------------------------------------------------------- /benchmark/run_test.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | from os import path 4 | import argparse 5 | 6 | 7 | def run_test( binary_name, model_name, n_tries, results_folder ): 8 | binary_base_name = path.splitext( path.basename( binary_name ) )[0] 9 | model_base_name = path.splitext( path.basename( model_name ) )[0] 10 | result_filename = path.join(results_folder, binary_base_name + "_" + model_base_name + ".txt") 11 | 12 | result_file = open(result_filename, "w") 13 | 14 | for _ in range(0, n_tries): 15 | proc = subprocess.Popen( [binary_name, model_name, "-o", "test.ply"], stdout=subprocess.PIPE ) 16 | output = proc.stdout.read().decode('utf-8').rstrip() 17 | result_file.write( "%s\n" % output ) 18 | 19 | result_file.close() 20 | 21 | 22 | def parse_arguments(): 23 | parser = argparse.ArgumentParser(description='Run single test') 24 | parser.add_argument('binary_name', help='Path to binary we will be executing') 25 | parser.add_argument('model_name', help='Name of ply file we wish to test on') 26 | parser.add_argument('--n_tries', type=int, help='Number of tries we wish to record', default=10) 27 | parser.add_argument('--results_folder', help='Folder where to store result file', default="./results/") 28 | return parser.parse_args() 29 | 30 | if __name__ == "__main__": 31 | args = parse_arguments() 32 | run_test( args.binary_name, args.model_name, args.n_tries, args.results_folder ) -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem A simple batch file for compiling the binaries to run the benchmarks 4 | 5 | IF "%~1"=="" ( 6 | echo "Please provide path to where `msh` library is stored. You can get `msh` from "https://github.com/mhalber/msh" 7 | exit /B 1 8 | ) 9 | 10 | set opts_c=-D_CRT_SECURE_NO_WARNINGS -nologo -FC -EHa- -GR- -O2 -W3 11 | set opts_cpp= -D_CRT_SECURE_NO_WARNINGS -nologo -FC -EHsc -O2 -W3 12 | set dev_dir=..\%~1 13 | set lib_dir=..\plylibs 14 | set tests_dir=..\tests 15 | set include_dirs=-I%dev_dir% -I%lib_dir% -I%tests_dir% 16 | echo %dev_dir% 17 | pushd bin 18 | 19 | cl %opts_cpp% %include_dirs% %tests_dir%\happly_test.cpp -Fe"happly_test.exe" 20 | cl %opts_cpp% %include_dirs% %tests_dir%\microply_test.cpp -Fe"microply_test.exe" 21 | cl %opts_cpp% %include_dirs% %tests_dir%\nanoply_test.cpp -Fe"nanoply_test.exe" 22 | cl %opts_cpp% %include_dirs% %tests_dir%\tinyply22_test.cpp -Fe"tinyply22_test.exe" 23 | cl %opts_cpp% %include_dirs% %tests_dir%\tinyply23_test.cpp -Fe"tinyply23_test.exe" 24 | 25 | cl %opts_cpp% %include_dirs% -I%lib_dir%\miniply %lib_dir%\miniply\miniply.cpp %tests_dir%\miniply_test.cpp -Fe"miniply_test.exe" 26 | cl %opts_cpp% %include_dirs% -I%lib_dir%\plylib %lib_dir%\plylib\plylib.cpp %tests_dir%\plylib_test.cpp -Fe"plylib_test.exe" 27 | cl %opts_cpp% %include_dirs% -I%lib_dir%\tinyply21 %lib_dir%\tinyply21\tinyply.cpp %tests_dir%\tinyply21_test.cpp -Fe"tinyply21_test.exe" 28 | 29 | cl %opts_c% -wd4267 -wd4244 -wd4101 -wd4996 %include_dirs% -I%lib_dir%\turkply %lib_dir%\turkply\ply_io.c %tests_dir%\turkply_test.c -Fe"turkply_test.exe" 30 | cl %opts_c% %include_dirs% -I%lib_dir%\rply %lib_dir%\rply\rply.c %tests_dir%\rply_test.c -Fe"rply_test.exe" 31 | cl %opts_c% %include_dirs% %tests_dir%\mshply_test.c -Fe"mshply_test.exe" 32 | popd 33 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # A simple makefile for compiling the binaries to run the benchmarks 2 | 3 | CC=gcc 4 | CPP=g++ 5 | CFLAGS=-O3 -march=native -std=c11 6 | CPPFLAGS=-O3 -march=native -std=c++11 7 | 8 | LIB_DIR=plylibs 9 | TESTS_DIR=tests 10 | INCLUDE_DIRS=-I${MSH_DIR} -I${LIB_DIR} -I${TESTS_DIR} 11 | 12 | ifeq ($(MSH_DIR),) 13 | all: 14 | @echo Please specify MSH_DIR variable pointing to folder where msh library is stored. You can get msh from "https://github.com/mhalber/msh" 15 | @echo Example: make MSH_DIR=/usr/local/include/ 16 | else 17 | all: 18 | $(CC) ${INCLUDE_DIRS} $(CFLAGS) ${TESTS_DIR}/mshply_test.c -o bin/mshply_test 19 | $(CC) ${INCLUDE_DIRS} -I${LIB_DIR}/turkply/ $(CFLAGS) ${LIB_DIR}/turkply/ply_io.c ${TESTS_DIR}/turkply_test.c -o bin/turkply_test 20 | $(CC) ${INCLUDE_DIRS} -I${LIB_DIR}/rply/ $(CFLAGS) ${LIB_DIR}/rply/rply.c ${TESTS_DIR}/rply_test.c -o bin/rply_test 21 | $(CPP) ${INCLUDE_DIRS} $(CPPFLAGS) ${TESTS_DIR}/happly_test.cpp -o bin/happly_test 22 | $(CPP) ${INCLUDE_DIRS} $(CPPFLAGS) ${TESTS_DIR}/microply_test.cpp -o bin/miniply_test 23 | $(CPP) ${INCLUDE_DIRS} -I${LIB_DIR}/nanoply/ $(CPPFLAGS) ${TESTS_DIR}/nanoply_test.cpp -o bin/nanoply_test 24 | $(CPP) ${INCLUDE_DIRS} -I${LIB_DIR}/tinyply21/ $(CPPFLAGS) ${LIB_DIR}/tinyply21/tinyply.cpp ${TESTS_DIR}/tinyply21_test.cpp -o bin/tinyply21_test 25 | $(CPP) ${INCLUDE_DIRS} -I${LIB_DIR}/tinyply22/ $(CPPFLAGS) ${TESTS_DIR}/tinyply22_test.cpp -o bin/tinyply22_test 26 | $(CPP) ${INCLUDE_DIRS} -I${LIB_DIR}/tinyply23/ $(CPPFLAGS) ${TESTS_DIR}/tinyply23_test.cpp -o bin/tinyply23_test 27 | $(CPP) ${INCLUDE_DIRS} -I${LIB_DIR}/plylib/ $(CPPFLAGS) ${LIB_DIR}/plylib/plylib.cpp ${TESTS_DIR}/plylib_test.cpp -o bin/plylib_test 28 | $(CPP) ${INCLUDE_DIRS} -I${LIB_DIR}/miniply/ $(CPPFLAGS) ${LIB_DIR}/miniply/miniply.cpp ${TESTS_DIR}/miniply_test.cpp -o bin/miniply_test 29 | endif 30 | -------------------------------------------------------------------------------- /plylibs/micro_ply/micro_ply.h: -------------------------------------------------------------------------------- 1 | /*Licensed under MIT or Public Domain. See bottom of file for details. 2 | 3 | micro_ply.h 4 | Written by Nick Klingensmith, @koujaku on Twitter, @maluoi on GitHub 5 | 6 | This is a small ASCII .ply loader and converter. It's intended to be as 7 | short as possible, while still being easily readable! The idea is that it 8 | can be trivially embedded in another single header library or single file 9 | project without too much trouble. 10 | 11 | Example usage: 12 | 13 | // micro_ply.h follows stb library conventions, so MICRO_PLY_IMPL must 14 | // be defined before include in only one file in your project! 15 | #define MICRO_PLY_IMPL 16 | #include "micro_ply.h" 17 | 18 | // This is the vertex layout we'll be converting the PLY to. 19 | typedef struct skg_vert_t { 20 | float pos [3]; 21 | float norm[3]; 22 | float uv [2]; 23 | uint8_t col [4]; 24 | } skg_vert_t; 25 | 26 | // Read the data from file, that's still on you, sorry :) 27 | void *data; 28 | size_t size; 29 | FILE *fp; 30 | if (fopen_s(&fp, filename, "rb") != 0 || fp == nullptr) { 31 | return false; 32 | } 33 | fseek(fp, 0L, SEEK_END); 34 | size = ftell(fp); 35 | rewind(fp); 36 | data = malloc(size); 37 | fread (data, size, 1, fp); 38 | fclose(fp); 39 | 40 | // Parse the data using ply_read 41 | ply_file_t file; 42 | if (!ply_read(data, size, &file)) 43 | return false; 44 | 45 | // Describe the way the contents of the PLY file map to our own vertex 46 | // format. If the property can't be found in the file, the default value 47 | // will be assigned. 48 | float fzero = 0; 49 | uint8_t white = 255; 50 | ply_map_t map_verts[] = { 51 | { PLY_PROP_POSITION_X, ply_prop_decimal, sizeof(float), 0, &fzero }, 52 | { PLY_PROP_POSITION_Y, ply_prop_decimal, sizeof(float), 4, &fzero }, 53 | { PLY_PROP_POSITION_Z, ply_prop_decimal, sizeof(float), 8, &fzero }, 54 | { PLY_PROP_NORMAL_X, ply_prop_decimal, sizeof(float), 12, &fzero }, 55 | { PLY_PROP_NORMAL_Y, ply_prop_decimal, sizeof(float), 16, &fzero }, 56 | { PLY_PROP_NORMAL_Z, ply_prop_decimal, sizeof(float), 20, &fzero }, 57 | { PLY_PROP_TEXCOORD_X, ply_prop_decimal, sizeof(float), 24, &fzero }, 58 | { PLY_PROP_TEXCOORD_Y, ply_prop_decimal, sizeof(float), 28, &fzero }, 59 | { PLY_PROP_COLOR_R, ply_prop_uint, sizeof(uint8_t), 32, &white }, 60 | { PLY_PROP_COLOR_G, ply_prop_uint, sizeof(uint8_t), 33, &white }, 61 | { PLY_PROP_COLOR_B, ply_prop_uint, sizeof(uint8_t), 34, &white }, 62 | { PLY_PROP_COLOR_A, ply_prop_uint, sizeof(uint8_t), 35, &white }, }; 63 | ply_convert(&file, PLY_ELEMENT_VERTICES, map_verts, _countof(map_verts), sizeof(skg_vert_t), (void **)out_verts, out_vert_count); 64 | 65 | // Properties defined as lists in the PLY format will get triangulated 66 | // during conversion, so you don't need to worry about quads or n-gons in 67 | // the geometry. 68 | uint32_t izero = 0; 69 | ply_map_t map_inds[] = { { PLY_PROP_INDICES, ply_prop_uint, sizeof(uint32_t), 0, &izero } }; 70 | ply_convert(&file, PLY_ELEMENT_FACES, map_inds, _countof(map_inds), sizeof(uint32_t), (void **)out_indices, out_ind_count); 71 | 72 | // You gotta free the memory manually! 73 | ply_free(&file); 74 | free(data); 75 | */ 76 | 77 | #pragma once 78 | 79 | #include 80 | 81 | #define PLY_PROP_POSITION_X "x" 82 | #define PLY_PROP_POSITION_Y "y" 83 | #define PLY_PROP_POSITION_Z "z" 84 | #define PLY_PROP_NORMAL_X "nx" 85 | #define PLY_PROP_NORMAL_Y "ny" 86 | #define PLY_PROP_NORMAL_Z "nz" 87 | #define PLY_PROP_TEXCOORD_X "s" 88 | #define PLY_PROP_TEXCOORD_Y "t" 89 | #define PLY_PROP_COLOR_R "red" 90 | #define PLY_PROP_COLOR_G "green" 91 | #define PLY_PROP_COLOR_B "blue" 92 | #define PLY_PROP_COLOR_A "alpha" 93 | #define PLY_PROP_INDICES "vertex_index" 94 | #define PLY_ELEMENT_VERTICES "vertex" 95 | #define PLY_ELEMENT_FACES "face" 96 | 97 | /////////////////////////////////////////// 98 | 99 | typedef enum ply_prop_ { 100 | ply_prop_int = 1, 101 | ply_prop_uint, 102 | ply_prop_decimal, 103 | } ply_prop_; 104 | 105 | typedef struct ply_prop_t { 106 | uint8_t bytes; 107 | uint8_t type; // follows ply_prop_ 108 | uint8_t list_bytes; 109 | uint8_t list_type; 110 | uint16_t offset; 111 | char name[32]; 112 | } ply_prop_t; 113 | 114 | typedef struct ply_element_t { 115 | char name[64]; 116 | int32_t count; 117 | ply_prop_t *properties; 118 | int32_t property_count; 119 | void *data; 120 | int32_t data_stride; 121 | void *list_data; 122 | } ply_element_t; 123 | 124 | typedef struct ply_file_t { 125 | ply_element_t *elements; 126 | int32_t count; 127 | } ply_file_t; 128 | 129 | typedef struct ply_map_t { 130 | const char *name; 131 | uint8_t to_type; 132 | uint8_t to_size; 133 | uint16_t to_offset; 134 | const void *default_val; 135 | } ply_map_t; 136 | 137 | /////////////////////////////////////////// 138 | 139 | bool ply_read (const void *data, size_t data_size, ply_file_t *out_file); 140 | void ply_free (ply_file_t *file); 141 | void ply_convert(const ply_file_t *file, const char *element_name, const ply_map_t *to_format, int32_t to_format_count, int32_t format_stride, void **out_data, int32_t *out_count); 142 | 143 | /////////////////////////////////////////// 144 | 145 | #ifdef MICRO_PLY_IMPL 146 | 147 | #include 148 | #include 149 | 150 | /////////////////////////////////////////// 151 | 152 | void _ply_convert(uint8_t *dest, uint8_t dest_size, uint8_t dest_type, const uint8_t *src, uint8_t src_size, uint8_t src_type) { 153 | if (dest_size == src_size && src_type == dest_type) memcpy(dest, src, dest_size); 154 | else if (src_type == ply_prop_decimal) { 155 | double val = src_size == 4 156 | ? *(float *)src 157 | : *(double *)src; 158 | if (dest_size == 4) *(float *)dest = (float)val; 159 | else *(double *)dest = (double)val; 160 | } else { 161 | int64_t val=0; 162 | if (src_type == ply_prop_int) { 163 | switch (src_size) { 164 | case 1: val = *(int8_t *)src; break; 165 | case 2: val = *(int16_t *)src; break; 166 | case 4: val = *(int32_t *)src; break; 167 | case 8: val = *(int64_t *)src; break;} 168 | } else { 169 | switch (src_size) { 170 | case 1: val = *(uint8_t *)src; break; 171 | case 2: val = *(uint16_t *)src; break; 172 | case 4: val = *(uint32_t *)src; break; 173 | case 8: val = *(uint64_t *)src; break;} 174 | } 175 | if (dest_type == ply_prop_int) { 176 | switch (dest_size) { 177 | case 1: *(int8_t *)dest = (int8_t)val; break; 178 | case 2: *(int16_t *)dest = (int16_t)val; break; 179 | case 4: *(int32_t *)dest = (int32_t)val; break;} 180 | } else { 181 | switch (dest_size) { 182 | case 1: *(uint8_t *)dest = (uint8_t)val; break; 183 | case 2: *(uint16_t*)dest = (uint16_t)val; break; 184 | case 4: *(uint32_t*)dest = (uint32_t)val; break;} 185 | } 186 | } 187 | } 188 | 189 | /////////////////////////////////////////// 190 | 191 | bool ply_read(const void *data, size_t data_size, ply_file_t *out_file) { 192 | // Support function, if string starts with 193 | bool (*starts_with)(const char *, const char *) = [](const char *str, const char *prefix) { 194 | while (*prefix) { 195 | if (*prefix++ != *str++) 196 | return false; 197 | } 198 | return true; 199 | }; 200 | // Support function, gets the following whitespace separated word 201 | void (*get_word)(char *start, char *out, size_t out_size) = [](char *start, char *out, size_t out_size) { 202 | int count = 0; 203 | while (*start != ' ' && *start != '\t' && *start != '\n' && *start != '\r' && *start != '\0' && count < (out_size - 1)) { 204 | out[count] = *start++; 205 | count++; 206 | } 207 | out[count] = '\0'; 208 | }; 209 | void (*type_info)(const char *, uint8_t *, uint8_t *) = [](const char *str, uint8_t *type, uint8_t *bytes) { 210 | if (strcmp(str, "char" ) == 0) { *bytes = 1; *type = ply_prop_int; } 211 | else if (strcmp(str, "uchar" ) == 0) { *bytes = 1; *type = ply_prop_uint; } 212 | else if (strcmp(str, "short" ) == 0) { *bytes = 2; *type = ply_prop_int; } 213 | else if (strcmp(str, "ushort") == 0) { *bytes = 2; *type = ply_prop_uint; } 214 | else if (strcmp(str, "int" ) == 0) { *bytes = 4; *type = ply_prop_int; } 215 | else if (strcmp(str, "uint" ) == 0) { *bytes = 4; *type = ply_prop_uint; } 216 | else if (strcmp(str, "float" ) == 0) { *bytes = 4; *type = ply_prop_decimal; } 217 | else if (strcmp(str, "double") == 0) { *bytes = 8; *type = ply_prop_decimal; } 218 | }; 219 | 220 | // Check file signature 221 | char *file = (char*)data; 222 | if (!starts_with(file, "ply")) 223 | return false; 224 | 225 | // File data 226 | int32_t format = 0; 227 | int32_t vert_count = 0; 228 | int32_t face_count = 0; 229 | out_file->count = 0; 230 | out_file->elements = nullptr; 231 | 232 | // Read the header 233 | char *line = strchr(file, '\n'); 234 | char word[128]; 235 | while(true) { 236 | if (!line) return false; 237 | line += 1; 238 | 239 | if (starts_with(line, "format ")) { 240 | get_word(line + sizeof("format"), word, sizeof(word)); 241 | if (strcmp(word, "ascii" ) == 0) format = 0; 242 | else if (strcmp(word, "binary_little_endian") == 0) format = 1; 243 | else if (strcmp(word, "binary_big_endian" ) == 0) format = 2; 244 | } else if (starts_with(line, "comment ")) { 245 | } else if (starts_with(line, "element ")) { 246 | ply_element_t el = {}; 247 | get_word(line + sizeof("element"), el.name, sizeof(el.name)); 248 | get_word(line + sizeof("element ") + strlen(el.name), word, sizeof(word)); 249 | el.count = atoi(word); 250 | 251 | out_file->count += 1; 252 | out_file->elements = (ply_element_t*)realloc(out_file->elements, sizeof(ply_element_t) * (out_file->count)); 253 | out_file->elements[out_file->count - 1] = el; 254 | } else if (starts_with(line, "property ")) { 255 | ply_prop_t prop = {}; 256 | get_word(line + sizeof("property"), word, sizeof(word)); 257 | 258 | if (strcmp(word, "list" ) == 0) { 259 | size_t off = sizeof("property ") + strlen(word); 260 | get_word(line + off, word, sizeof(word)); 261 | type_info(word, &prop.type, &prop.bytes); 262 | off += strlen(word) + 1; 263 | get_word(line + off, word, sizeof(word)); 264 | type_info(word, &prop.list_type, &prop.list_bytes); 265 | off += strlen(word) + 1; 266 | get_word(line + off, prop.name, sizeof(prop.name)); 267 | } else { 268 | type_info(word, &prop.type, &prop.bytes); 269 | get_word(line + sizeof("property ") + strlen(word), prop.name, sizeof(prop.name)); 270 | } 271 | 272 | ply_element_t *el = &out_file->elements[out_file->count-1]; 273 | prop.offset = el->data_stride; 274 | el->data_stride += prop.bytes; 275 | el->property_count += 1; 276 | el->properties = (ply_prop_t*)realloc(el->properties, sizeof(ply_prop_t) * el->property_count); 277 | el->properties[el->property_count-1] = prop; 278 | } else if (starts_with(line, "end_header")) { 279 | line = strchr(line, '\n')+1; 280 | break; 281 | } 282 | line = strchr(line, '\n'); 283 | } 284 | 285 | // Parse the data 286 | for (int32_t i = 0; i < out_file->count; i++) { 287 | ply_element_t *el = &out_file->elements[i]; 288 | el->data = malloc(el->data_stride*el->count); 289 | uint8_t *data = (uint8_t*)el->data; 290 | 291 | // If it's a list type 292 | if (el->property_count == 1 && el->properties[0].list_type != 0) { 293 | int32_t list_cap = el->count * 4; 294 | int32_t list_count = 0; 295 | el->list_data = malloc(el->properties[0].list_bytes * list_cap); 296 | uint8_t *list_data = (uint8_t*)el->list_data; 297 | 298 | for (int32_t e = 0; e < el->count; e++) { 299 | size_t off = 0; 300 | get_word(line + off, word, sizeof(word)); 301 | off += strlen(word) + 1; 302 | int32_t count = atoi(word); 303 | _ply_convert(data, el->properties[0].bytes, el->properties[0].type, (uint8_t*)&count, sizeof(double), ply_prop_int); 304 | for (size_t c = 0; c < count; c++) { 305 | get_word(line + off, word, sizeof(word)); 306 | off += strlen(word) + 1; 307 | if (el->properties[0].type == ply_prop_decimal) { 308 | double val = atof(word); 309 | _ply_convert(list_data, el->properties[0].list_bytes, el->properties[0].list_type, (uint8_t*)&val, sizeof(double), ply_prop_decimal); 310 | } else if (el->properties[0].type == ply_prop_int) { 311 | int64_t val = atol(word); 312 | _ply_convert(list_data, el->properties[0].list_bytes, el->properties[0].list_type, (uint8_t*)&val, sizeof(int64_t), ply_prop_int); 313 | } else { 314 | uint64_t val = atol(word); 315 | _ply_convert(list_data, el->properties[0].list_bytes, el->properties[0].list_type, (uint8_t*)&val, sizeof(uint64_t), ply_prop_uint); 316 | } 317 | list_data += el->properties[0].list_bytes; 318 | list_count += 1; 319 | if (list_count >= list_cap) { 320 | list_cap = (int32_t)(list_cap * 1.25f); 321 | el->list_data = realloc(el->list_data, el->properties[0].list_bytes * list_cap); 322 | list_data = ((uint8_t*)el->list_data) + (list_count * el->properties[0].list_bytes); 323 | } 324 | } 325 | line = strchr(line, '\n') + 1; 326 | data += el->properties[0].bytes; 327 | } 328 | } else { 329 | for (int32_t e = 0; e < el->count; e++) { 330 | size_t off = 0; 331 | for (int32_t p = 0; p < el->property_count; p++) { 332 | get_word(line + off, word, sizeof(word)); 333 | off += strlen(word) + 1; 334 | if (el->properties[p].type == ply_prop_decimal) { 335 | double val = atof(word); 336 | _ply_convert(data+el->properties[p].offset, el->properties[p].bytes, el->properties[p].type, (uint8_t*)&val, sizeof(double), ply_prop_decimal); 337 | } else if (el->properties[p].type == ply_prop_int) { 338 | int64_t val = atol(word); 339 | _ply_convert(data+el->properties[p].offset, el->properties[p].bytes, el->properties[p].type, (uint8_t*)&val, sizeof(int64_t), ply_prop_int); 340 | } else { 341 | uint64_t val = atol(word); 342 | _ply_convert(data+el->properties[p].offset, el->properties[p].bytes, el->properties[p].type, (uint8_t*)&val, sizeof(uint64_t), ply_prop_uint); 343 | } 344 | } 345 | line = strchr(line, '\n') + 1; 346 | data += el->data_stride; 347 | } 348 | } 349 | } 350 | return true; 351 | } 352 | 353 | /////////////////////////////////////////// 354 | 355 | void ply_convert(const ply_file_t *file, const char *element_name, const ply_map_t *to_format, int32_t format_count, int32_t format_stride, void **out_data, int32_t *out_count) { 356 | *out_data = nullptr; 357 | *out_count = 0; 358 | 359 | // Find the elements we want to convert by name 360 | const ply_element_t *elements = nullptr; 361 | for (size_t i = 0; i < file->count; i++) { 362 | if (strcmp(file->elements[i].name, element_name) == 0) { 363 | elements = &file->elements[i]; 364 | break; 365 | } 366 | } 367 | if (elements == nullptr) 368 | return; 369 | 370 | if (elements->list_data == nullptr) { 371 | // Map the element properties to the out format 372 | int32_t *map = (int32_t*)malloc(sizeof(int32_t) * format_count); 373 | for (int32_t i = 0; i < format_count; i++) { 374 | map[i] = -1; 375 | for (int32_t p = 0; p < elements->property_count; p++) { 376 | if (strcmp(elements->properties[p].name, to_format[i].name) == 0) { 377 | map[i] = p; 378 | break; 379 | } 380 | } 381 | } 382 | 383 | // Now convert and copy each item 384 | *out_data = malloc(elements->count * format_stride); 385 | *out_count = elements->count; 386 | uint8_t *src = (uint8_t*)elements->data; 387 | uint8_t *dest = (uint8_t*)*out_data; 388 | for (int32_t i = 0; i < elements->count; i++) { 389 | for (int32_t f = 0; f < format_count; f++) { 390 | if (map[f] == -1) { 391 | memcpy(dest+to_format[f].to_offset, to_format[f].default_val, to_format[f].to_size); 392 | } else { 393 | ply_prop_t *prop = &elements->properties[map[f]]; 394 | _ply_convert( 395 | dest+to_format[f].to_offset, to_format[f].to_size, to_format[f].to_type, 396 | src+prop->offset, prop->bytes, prop->type); 397 | } 398 | } 399 | src += elements->data_stride; 400 | dest += format_stride; 401 | } 402 | 403 | free(map); 404 | } else { 405 | uint8_t src_size = elements->properties[0].bytes; 406 | uint8_t src_type = elements->properties[0].type; 407 | uint8_t src_ind_size = elements->properties[0].list_bytes; 408 | uint8_t src_ind_type = elements->properties[0].list_type; 409 | // Count how many total indices there will be 410 | int64_t count = 0; 411 | uint8_t *src = (uint8_t *)elements->data; 412 | for (int32_t i = 0; i < elements->count; i++) { 413 | uint32_t ct = 0; 414 | _ply_convert((uint8_t *)&ct, sizeof(uint32_t), ply_prop_uint, src, src_size, src_type); 415 | count += 3 + ((int64_t)ct-3)*3; 416 | src += src_size; 417 | } 418 | 419 | *out_data = malloc(count * format_stride); 420 | *out_count = (int32_t)count; 421 | src = (uint8_t*)elements->data; 422 | uint8_t *src_ind = (uint8_t*)elements->list_data; 423 | uint8_t *dest = (uint8_t*)*out_data; 424 | for (int32_t i = 0; i < elements->count; i++) { 425 | uint32_t ct = 0; 426 | _ply_convert((uint8_t *)&ct, sizeof(uint32_t), ply_prop_uint, src, src_size, src_type); 427 | for (uint32_t x = 0; x < ct-2; x++) { 428 | _ply_convert( 429 | dest+(x*3*to_format[0].to_size), to_format[0].to_size, to_format[0].to_type, 430 | src_ind, src_ind_size, src_ind_type); 431 | _ply_convert( 432 | dest+((x*3+1)*to_format[0].to_size), to_format[0].to_size, to_format[0].to_type, 433 | src_ind+((x+1)*src_ind_size), src_ind_size, src_ind_type); 434 | _ply_convert( 435 | dest+((x*3+2)*to_format[0].to_size), to_format[0].to_size, to_format[0].to_type, 436 | src_ind+((x+2)*src_ind_size), src_ind_size, src_ind_type); 437 | } 438 | src_ind += src_ind_size * ct; 439 | dest += format_stride * (3 + (ct-3)*3); 440 | src += src_size; 441 | } 442 | } 443 | } 444 | 445 | /////////////////////////////////////////// 446 | 447 | void ply_free(ply_file_t *file) { 448 | for (int32_t i = 0; i < file->count; i++) { 449 | free(file->elements[i].data); 450 | free(file->elements[i].list_data); 451 | free(file->elements[i].properties); 452 | } 453 | free(file->elements); 454 | } 455 | 456 | #endif 457 | 458 | /* 459 | ------------------------------------------------------------------------------ 460 | This software is available under 2 licenses -- choose whichever you prefer. 461 | ------------------------------------------------------------------------------ 462 | ALTERNATIVE A - MIT License 463 | Copyright (c) 2020 Nick Klingensmith 464 | Permission is hereby granted, free of charge, to any person obtaining a copy of 465 | this software and associated documentation files (the "Software"), to deal in 466 | the Software without restriction, including without limitation the rights to 467 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 468 | of the Software, and to permit persons to whom the Software is furnished to do 469 | so, subject to the following conditions: 470 | The above copyright notice and this permission notice shall be included in all 471 | copies or substantial portions of the Software. 472 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 473 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 474 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 475 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 476 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 477 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 478 | SOFTWARE. 479 | ------------------------------------------------------------------------------ 480 | ALTERNATIVE B - Public Domain (www.unlicense.org) 481 | This is free and unencumbered software released into the public domain. 482 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 483 | software, either in source code form or as a compiled binary, for any purpose, 484 | commercial or non-commercial, and by any means. 485 | In jurisdictions that recognize copyright laws, the author or authors of this 486 | software dedicate any and all copyright interest in the software to the public 487 | domain. We make this dedication for the benefit of the public at large and to 488 | the detriment of our heirs and successors. We intend this dedication to be an 489 | overt act of relinquishment in perpetuity of all present and future rights to 490 | this software under copyright law. 491 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 492 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 493 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 494 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 495 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 496 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 497 | ------------------------------------------------------------------------------ 498 | */ -------------------------------------------------------------------------------- /plylibs/miniply/miniply.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 Vilya Harvey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | /// miniply - A simple and fast parser for PLY files 33 | /// ================================================ 34 | /// 35 | /// For details about the PLY format see: 36 | /// * http://paulbourke.net/dataformats/ply/ 37 | /// * https://en.wikipedia.org/wiki/PLY_(file_format) 38 | 39 | namespace miniply { 40 | 41 | // 42 | // Constants 43 | // 44 | 45 | static constexpr uint32_t kInvalidIndex = 0xFFFFFFFFu; 46 | 47 | // Standard PLY element names 48 | extern const char* kPLYVertexElement; // "vertex" 49 | extern const char* kPLYFaceElement; // "face" 50 | 51 | 52 | // 53 | // PLY Parsing types 54 | // 55 | 56 | enum class PLYFileType { 57 | ASCII, 58 | Binary, 59 | BinaryBigEndian, 60 | }; 61 | 62 | 63 | enum class PLYPropertyType { 64 | Char, 65 | UChar, 66 | Short, 67 | UShort, 68 | Int, 69 | UInt, 70 | Float, 71 | Double, 72 | 73 | None, //!< Special value used in Element::listCountType to indicate a non-list property. 74 | }; 75 | 76 | 77 | struct PLYProperty { 78 | std::string name; 79 | PLYPropertyType type = PLYPropertyType::None; //!< Type of the data. Must be set to a value other than None. 80 | PLYPropertyType countType = PLYPropertyType::None; //!< None indicates this is not a list type, otherwise it's the type for the list count. 81 | uint32_t offset = 0; //!< Byte offset from the start of the row. 82 | uint32_t stride = 0; 83 | 84 | std::vector listData; 85 | std::vector rowCount; // Entry `i` is the number of items (*not* the number of bytes) in row `i`. 86 | }; 87 | 88 | 89 | struct PLYElement { 90 | std::string name; //!< Name of this element. 91 | std::vector properties; 92 | uint32_t count = 0; //!< The number of items in this element (e.g. the number of vertices if this is the vertex element). 93 | bool fixedSize = true; //!< `true` if there are only fixed-size properties in this element, i.e. no list properties. 94 | uint32_t rowStride = 0; //!< The number of bytes from the start of one row to the start of the next, for this element. 95 | 96 | void calculate_offsets(); 97 | 98 | /// Returns the index for the named property in this element, or `kInvalidIndex` 99 | /// if it can't be found. 100 | uint32_t find_property(const char* propName) const; 101 | 102 | /// Return the indices for several properties in one go. Use it like this: 103 | /// ``` 104 | /// uint32_t indexes[3]; 105 | /// if (elem.find_properties(indexes, 3, "foo", "bar", "baz")) { ... } 106 | /// ``` 107 | /// `propIdxs` is where the property indexes will be stored. `numIdxs` is 108 | /// the number of properties we will look up. There must be exactly 109 | /// `numIdxs` parameters after `numIdxs`; each of the is a c-style string 110 | /// giving the name of a property. 111 | /// 112 | /// The return value will be true if all properties were found. If it was 113 | /// not true, you should not use any values from propIdxs. 114 | bool find_properties(uint32_t propIdxs[], uint32_t numIdxs, ...) const; 115 | 116 | /// Same as `find_properties`, for when you already have a `va_list`. This 117 | /// is called internally by both `PLYElement::find_properties` and 118 | /// `PLYReader::find_properties`. 119 | bool find_properties_va(uint32_t propIdxs[], uint32_t numIdxs, va_list names) const; 120 | 121 | /// Call this on the element at some point before you load its data, when 122 | /// you know that every row's list will have the same length. It will 123 | /// replace the single variable-size property with a set of new fixed-size 124 | /// properties: one for the list count, followed by one for each of the 125 | /// list values. This will allow miniply to load and extract the property 126 | /// data a lot more efficiently, giving a big performance increase. 127 | /// 128 | /// After you've called this, you must use PLYReader's `extract_columns` 129 | /// method to get the data, rather than `extract_list_column`. 130 | /// 131 | /// The `newPropIdxs` parameter must be an array with at least `listSize` 132 | /// entries. If the function returns true, this will have been populated 133 | /// with the indices of the new properties that represent the list values 134 | /// (i.e. not including the list count property, which will have the same 135 | /// index as the old list property). 136 | /// 137 | /// The function returns false if the property index is invalid, or the 138 | /// property it refers to is not a list property. In these cases it will 139 | /// not modify anything. Otherwise it will return true. 140 | bool convert_list_to_fixed_size(uint32_t listPropIdx, uint32_t listSize, uint32_t newPropIdxs[]); 141 | }; 142 | 143 | 144 | class PLYReader { 145 | public: 146 | PLYReader(const char* filename); 147 | ~PLYReader(); 148 | 149 | bool valid() const; 150 | bool has_element() const; 151 | const PLYElement* element() const; 152 | bool load_element(); 153 | void next_element(); 154 | 155 | PLYFileType file_type() const; 156 | int version_major() const; 157 | int version_minor() const; 158 | uint32_t num_elements() const; 159 | uint32_t find_element(const char* name) const; 160 | PLYElement* get_element(uint32_t idx); 161 | 162 | /// Check whether the current element has the given name. 163 | bool element_is(const char* name) const; 164 | 165 | /// Number of rows in the current element. 166 | uint32_t num_rows() const; 167 | 168 | /// Returns the index for the named property in the current element, or 169 | /// `kInvalidIndex` if it can't be found. 170 | uint32_t find_property(const char* name) const; 171 | 172 | /// Equivalent to calling `find_properties` on the current element. 173 | bool find_properties(uint32_t propIdxs[], uint32_t numIdxs, ...) const; 174 | 175 | /// Copy the data for the specified properties into `dest`, which must be 176 | /// an array with at least enough space to hold all of the extracted column 177 | /// data. `propIdxs` is an array containing the indexes of the properties 178 | /// to copy; it has `numProps` elements. 179 | /// 180 | /// `destType` specifies the data type for values stored in `dest`. All 181 | /// property values will be converted to this type if necessary. 182 | /// 183 | /// This function does some checks up front to pick the most efficient code 184 | /// path for extracting the data. It considers: 185 | /// (a) whether any data conversion is required. 186 | /// (b) whether all property values to be extracted are in contiguous 187 | /// memory locations for any given item. 188 | /// (c) whether the data for all rows is contiguous in memory. 189 | /// In the best case it reduces to a single memcpy call. In the worst case 190 | /// we must iterate over all values to be copied, applying type conversions 191 | /// as we go. 192 | /// 193 | /// Note that this function does not handle list-valued properties. Use 194 | /// `extract_list_column()` for those instead. 195 | bool extract_properties(const uint32_t propIdxs[], uint32_t numProps, PLYPropertyType destType, void* dest) const; 196 | 197 | /// Get the array of item counts for a list property. Entry `i` in this 198 | /// array is the number of items in the `i`th list. 199 | const uint32_t* get_list_counts(uint32_t propIdx) const; 200 | 201 | const uint8_t* get_list_data(uint32_t propIdx) const; 202 | bool extract_list_property(uint32_t propIdx, PLYPropertyType destType, void* dest) const; 203 | 204 | uint32_t num_triangles(uint32_t propIdx) const; 205 | bool requires_triangulation(uint32_t propIdx) const; 206 | bool extract_triangles(uint32_t propIdx, const float pos[], uint32_t numVerts, PLYPropertyType destType, void* dest) const; 207 | 208 | bool find_pos(uint32_t propIdxs[3]) const; 209 | bool find_normal(uint32_t propIdxs[3]) const; 210 | bool find_texcoord(uint32_t propIdxs[2]) const; 211 | bool find_color(uint32_t propIdxs[3]) const; 212 | bool find_indices(uint32_t propIdxs[1]) const; 213 | 214 | private: 215 | bool refill_buffer(); 216 | bool rewind_to_safe_char(); 217 | bool accept(); 218 | bool advance(); 219 | bool next_line(); 220 | bool match(const char* str); 221 | bool which(const char* values[], uint32_t* index); 222 | bool which_property_type(PLYPropertyType* type); 223 | bool keyword(const char* kw); 224 | bool identifier(char* dest, size_t destLen); 225 | 226 | template // T must be a type compatible with uint32_t. 227 | bool typed_which(const char* values[], T* index) { 228 | return which(values, reinterpret_cast(index)); 229 | } 230 | 231 | bool int_literal(int* value); 232 | bool float_literal(float* value); 233 | bool double_literal(double* value); 234 | 235 | bool parse_elements(); 236 | bool parse_element(); 237 | bool parse_property(std::vector& properties); 238 | 239 | bool load_fixed_size_element(PLYElement& elem); 240 | bool load_variable_size_element(PLYElement& elem); 241 | 242 | bool load_ascii_scalar_property(PLYProperty& prop, size_t& destIndex); 243 | bool load_ascii_list_property(PLYProperty& prop); 244 | bool load_binary_scalar_property(PLYProperty& prop, size_t& destIndex); 245 | bool load_binary_list_property(PLYProperty& prop); 246 | bool load_binary_scalar_property_big_endian(PLYProperty& prop, size_t& destIndex); 247 | bool load_binary_list_property_big_endian(PLYProperty& prop); 248 | 249 | bool ascii_value(PLYPropertyType propType, uint8_t value[8]); 250 | 251 | private: 252 | FILE* m_f = nullptr; 253 | char* m_buf = nullptr; 254 | const char* m_bufEnd = nullptr; 255 | const char* m_pos = nullptr; 256 | const char* m_end = nullptr; 257 | bool m_inDataSection = false; 258 | bool m_atEOF = false; 259 | int64_t m_bufOffset = 0; 260 | 261 | bool m_valid = false; 262 | 263 | PLYFileType m_fileType = PLYFileType::ASCII; //!< Whether the file was ascii, binary little-endian, or binary big-endian. 264 | int m_majorVersion = 0; 265 | int m_minorVersion = 0; 266 | std::vector m_elements; //!< Element descriptors for this file. 267 | 268 | size_t m_currentElement = 0; 269 | bool m_elementLoaded = false; 270 | std::vector m_elementData; 271 | 272 | char* m_tmpBuf = nullptr; 273 | }; 274 | 275 | 276 | /// Given a polygon with `n` vertices, where `n` > 3, triangulate it and 277 | /// store the indices for the resulting triangles in `dst`. The `pos` 278 | /// parameter is the array of all vertex positions for the mesh; `indices` is 279 | /// the list of `n` indices for the polygon we're triangulating; and `dst` is 280 | /// where we write the new indices to. 281 | /// 282 | /// The triangulation will always produce `n - 2` triangles, so `dst` must 283 | /// have enough space for `3 * (n - 2)` indices. 284 | /// 285 | /// If `n == 3`, we simply copy the input indices to `dst`. If `n < 3`, 286 | /// nothing gets written to dst. 287 | /// 288 | /// The return value is the number of triangles. 289 | uint32_t triangulate_polygon(uint32_t n, const float pos[], uint32_t numVerts, const int indices[], int dst[]); 290 | 291 | } // namespace miniply 292 | -------------------------------------------------------------------------------- /plylibs/plylib/plylib.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * VCGLib o o * 3 | * Visual and Computer Graphics Library o o * 4 | * _ O _ * 5 | * Copyright(C) 2004-2016 \/)\/ * 6 | * Visual Computing Lab /\/| * 7 | * ISTI - Italian National Research Council | * 8 | * \ * 9 | * All rights reserved. * 10 | * * 11 | * This program is free software; you can redistribute it and/or modify * 12 | * it under the terms of the GNU General Public License as published by * 13 | * the Free Software Foundation; either version 2 of the License, or * 14 | * (at your option) any later version. * 15 | * * 16 | * This program is distributed in the hope that it will be useful, * 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 19 | * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * 20 | * for more details. * 21 | * * 22 | ****************************************************************************/ 23 | /**************************************************************************** 24 | Acknowlegments 25 | Portions of this file were based on the original code of the Ply library 26 | of Greg Turk and on the work of Claudio Rocchini 27 | 28 | ****************************************************************************/ 29 | /**************************************************************************** 30 | History 31 | 32 | $Log: not supported by cvs2svn $ 33 | Revision 1.5 2005/11/26 00:12:25 cignoni 34 | added prototype of interpret_texture_name 35 | 36 | Revision 1.4 2005/11/12 07:07:47 cignoni 37 | Changed Offset types to remove warnings 38 | 39 | Revision 1.3 2005/01/03 10:35:59 cignoni 40 | Improved the compatibility for ply format for faces having the list size (e.g. number of vertexes of a face) as a char instead of a uchar. 41 | Added a couple of new face descriptors, corrected a bug in error reporting function (and restructured) and translated a few comments. 42 | Thanks to Patrick Min for the careful bug reporting 43 | 44 | Revision 1.2 2004/04/27 13:29:19 turini 45 | *** empty log message *** 46 | 47 | Revision 1.1 2004/03/03 15:00:51 cignoni 48 | Initial commit 49 | 50 | ****************************************************************************/ 51 | 52 | #ifndef __VCG_PLYLIB 53 | #define __VCG_PLYLIB 54 | 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | namespace vcg { 61 | namespace ply { 62 | 63 | // Data types supported by the ply format 64 | enum PlyTypes { 65 | T_NOTYPE, 66 | T_CHAR, 67 | T_SHORT, 68 | T_INT, 69 | T_UCHAR, 70 | T_USHORT, 71 | T_UINT, 72 | T_FLOAT, 73 | T_DOUBLE, 74 | T_MAXTYPE 75 | }; 76 | 77 | // Error codes reported by GetError 78 | enum PlyError { 79 | E_NOERROR, // 0 80 | // Errors of open(..) 81 | E_CANTOPEN, // 1 82 | E_NOTHEADER, // 2 83 | E_UNESPECTEDEOF, // 3 84 | E_NOFORMAT, // 4 85 | E_SYNTAX, // 5 86 | E_PROPOUTOFELEMENT, // 6 87 | E_BADTYPENAME, // 7 88 | // Errors of addtoread(..) 89 | E_ELEMNOTFOUND, // 8 90 | E_PROPNOTFOUND, // 9 91 | E_BADTYPE, // 10 92 | E_INCOMPATIBLETYPE, // 11 93 | E_BADCAST, // 12 94 | E_MAXPLYERRORS 95 | }; 96 | 97 | // file formats supported by the ply format 98 | enum PlyFormat { 99 | F_UNSPECIFIED, 100 | F_ASCII, 101 | F_BINLITTLE, 102 | F_BINBIG 103 | }; 104 | 105 | 106 | #ifdef USE_ZLIB 107 | typedef void * GZFILE; 108 | #else 109 | typedef FILE * GZFILE; 110 | #endif 111 | 112 | 113 | // Messaggio di errore 114 | //extern const char * ply_error_msg[]; 115 | 116 | // TIPO FILE 117 | 118 | 119 | // Descrittore esterno di propieta' 120 | class PropDescriptor 121 | { 122 | public: 123 | const char * elemname; // Nome dell'elemento 124 | const char * propname; // Nome della propieta' 125 | int stotype1; // Tipo dell'elemento su file (se lista tipo degli elementi della lista) 126 | int memtype1; // Tipo dell'elemento in memoria (se lista tipo degli elementi della lista) 127 | size_t offset1; // Offset del valore in memoria 128 | int islist; // 1 se lista, 0 altrimenti 129 | int alloclist; // 1 se alloca lista, 0 se preallocata 130 | int stotype2; // Tipo del numero di elementi della lista su file 131 | int memtype2; // Tipo del numero di elementi della lista in memoria 132 | size_t offset2; // Offset valore memoria 133 | 134 | int format; // duplicazione del formato 135 | 136 | size_t stotypesize() const; // per sapere quanto e'grande un dato descrittore sul file 137 | size_t memtypesize() const; // per sapere quanto e'grande un dato descrittore in memoria 138 | const char *memtypename() const; 139 | const char *stotypename() const; 140 | }; 141 | 142 | // Reading Callback (used to copy a data prop) 143 | typedef bool (* readelemcb) ( GZFILE fp, void * mem, PropDescriptor * p ); 144 | 145 | class PlyProperty 146 | { 147 | public: 148 | inline PlyProperty() 149 | { 150 | tipo = 0; 151 | islist = 0; 152 | tipoindex = 0; 153 | bestored = 0; 154 | } 155 | 156 | inline PlyProperty( const char * na, int ti, int isl, int t2 ) 157 | { 158 | assert(na); 159 | assert(ti>0); 160 | assert(ti0 || (t2==0 && isl==0) ); 162 | assert(t2=0); 196 | 197 | name = std::string(na); 198 | number = nu; 199 | } 200 | 201 | 202 | inline void SetName( const char * na ) 203 | { 204 | name = std::string(na); 205 | } 206 | 207 | inline void SetNumbert( int nu ) 208 | { 209 | assert(nu>0); 210 | number = nu; 211 | } 212 | 213 | void AddProp( const char * na, int ti, int isl, int t2 ); 214 | 215 | int AddToRead( 216 | const char * propname, 217 | int stotype1, 218 | int memtype1, 219 | size_t offset1, 220 | int islist, 221 | int alloclist, 222 | int stotype2, 223 | int memtype2, 224 | size_t offset2 225 | ); // Vedi struttura PropDescriptor 226 | 227 | PlyProperty * FindProp( const char * name ); 228 | 229 | std::string name; // Nome dell'elemento 230 | int number; // Numero di elementi di questo tipo 231 | 232 | std::vector props; // Vettore dinamico delle property 233 | }; 234 | 235 | 236 | class PlyFile 237 | { 238 | private: 239 | void compile( PlyElement * e ); 240 | void compile( PlyProperty * p ); 241 | 242 | public: 243 | // Modi di apertura 244 | enum { 245 | MODE_READ, 246 | MODE_WRITE 247 | }; 248 | 249 | PlyFile(); 250 | ~PlyFile(); 251 | 252 | // Apre un file ply 253 | int Open( const char * filename, int mode ); 254 | // Chiude un file e disalloca la memoria 255 | void Destroy(); 256 | // Ritorna il codice dell'ultimo errore 257 | inline int GetError() const { return error; } 258 | // Definizione di lettura (Vedi struttura PropDescriptor) 259 | int AddToRead( 260 | const char * elemname, 261 | const char * propname, 262 | int stotype1, 263 | int memtype1, 264 | size_t offset1, 265 | int islist, 266 | int alloclist, 267 | int stotype2, 268 | int memtype2, 269 | size_t offset2 270 | ); 271 | // Come sopra ma con descrittore 272 | inline int AddToRead( const PropDescriptor & p ) 273 | { 274 | return AddToRead(p.elemname,p.propname,p.stotype1, 275 | p.memtype1,p.offset1,p.islist,p.alloclist,p.stotype2, 276 | p.memtype2,p.offset2 277 | ); 278 | } 279 | 280 | // Ritorna il numero di oggetti di un tipo di elemento 281 | const char * ElemName( int i ); 282 | 283 | int ElemNumber( int i ) const; 284 | // Setta il tipo di elemento corrente per effetture 285 | // la lettura 286 | inline void SetCurElement( int i ) 287 | { 288 | if(i<0 || i>=int(elements.size())) cure = 0; 289 | else 290 | { 291 | cure = &(elements[i]); 292 | compile(cure); 293 | } 294 | } 295 | // Lettura du un elemento 296 | int Read( void * mem ); 297 | 298 | std::vector elements; // Vettore degli elementi 299 | std::vector comments; // Vettore dei commenti 300 | static const char * typenames[9]; 301 | static const char * newtypenames[9]; 302 | 303 | inline const char * GetHeader() const { return header.c_str(); } 304 | inline int GetFormat() { return this->format; } 305 | protected: 306 | 307 | GZFILE gzfp; 308 | 309 | float version; // Versione del file 310 | int error; // Errore corrente (vedi enum) 311 | int format; // Formato del file (vedi enum ) 312 | 313 | std::string header; // Testo dell'header 314 | 315 | PlyElement * cure; // Elemento da leggere 316 | 317 | // Callback di lettura: vale ReadBin o ReadAcii 318 | int (* ReadCB)( GZFILE fp, const PlyProperty * r, void * mem, int fmt ); 319 | 320 | int OpenRead( const char * filename ); 321 | int OpenWrite( const char * filename ); 322 | 323 | PlyElement * AddElement( const char * name, int number ); 324 | int FindType( const char * name ) const; 325 | PlyElement * FindElement( const char * name ); 326 | }; 327 | 328 | void interpret_texture_name(const char*a, const char*fn, char*output); 329 | 330 | } // end namespace ply 331 | } // end namespace vcg 332 | #endif 333 | -------------------------------------------------------------------------------- /plylibs/rply/rply.h: -------------------------------------------------------------------------------- 1 | #ifndef RPLY_H 2 | #define RPLY_H 3 | /* ---------------------------------------------------------------------- 4 | * RPly library, read/write PLY files 5 | * Diego Nehab, IMPA 6 | * http://www.impa.br/~diego/software/rply 7 | * 8 | * This library is distributed under the MIT License. See notice 9 | * at the end of this file. 10 | * ---------------------------------------------------------------------- */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #define RPLY_VERSION "RPly 1.1.4" 17 | #define RPLY_COPYRIGHT "Copyright (C) 2003-2015 Diego Nehab" 18 | #define RPLY_AUTHORS "Diego Nehab" 19 | 20 | /* ---------------------------------------------------------------------- 21 | * Types 22 | * ---------------------------------------------------------------------- */ 23 | /* structures are opaque */ 24 | typedef struct t_ply_ *p_ply; 25 | typedef struct t_ply_element_ *p_ply_element; 26 | typedef struct t_ply_property_ *p_ply_property; 27 | typedef struct t_ply_argument_ *p_ply_argument; 28 | 29 | /* ply format mode type */ 30 | typedef enum e_ply_storage_mode_ { 31 | PLY_BIG_ENDIAN, 32 | PLY_LITTLE_ENDIAN, 33 | PLY_ASCII, 34 | PLY_DEFAULT /* has to be the last in enum */ 35 | } e_ply_storage_mode; /* order matches ply_storage_mode_list */ 36 | 37 | /* ply data type */ 38 | typedef enum e_ply_type { 39 | PLY_INT8, PLY_UINT8, PLY_INT16, PLY_UINT16, 40 | PLY_INT32, PLY_UIN32, PLY_FLOAT32, PLY_FLOAT64, 41 | PLY_CHAR, PLY_UCHAR, PLY_SHORT, PLY_USHORT, 42 | PLY_INT, PLY_UINT, PLY_FLOAT, PLY_DOUBLE, 43 | PLY_LIST /* has to be the last in enum */ 44 | } e_ply_type; /* order matches ply_type_list */ 45 | 46 | /* ---------------------------------------------------------------------- 47 | * Error callback prototype 48 | * 49 | * message: error message 50 | * ply: handle returned by ply_open or ply_create 51 | * ---------------------------------------------------------------------- */ 52 | typedef void (*p_ply_error_cb)(p_ply ply, const char *message); 53 | 54 | /* ---------------------------------------------------------------------- 55 | * Gets user data from within an error callback 56 | * 57 | * ply: handle returned by ply_open or ply_create 58 | * idata,pdata: contextual information set in ply_open or ply_create 59 | * ---------------------------------------------------------------------- */ 60 | int ply_get_ply_user_data(p_ply ply, void **pdata, long *idata); 61 | 62 | /* ---------------------------------------------------------------------- 63 | * Opens a PLY file for reading (fails if file is not a PLY file) 64 | * 65 | * name: file name 66 | * error_cb: error callback function 67 | * idata,pdata: contextual information available to users 68 | * 69 | * Returns 1 if successful, 0 otherwise 70 | * ---------------------------------------------------------------------- */ 71 | p_ply ply_open(const char *name, p_ply_error_cb error_cb, long idata, 72 | void *pdata); 73 | 74 | /* ---------------------------------------------------------------------- 75 | * Reads and parses the header of a PLY file returned by ply_open 76 | * 77 | * ply: handle returned by ply_open 78 | * 79 | * Returns 1 if successfull, 0 otherwise 80 | * ---------------------------------------------------------------------- */ 81 | int ply_read_header(p_ply ply); 82 | 83 | /* ---------------------------------------------------------------------- 84 | * Property reading callback prototype 85 | * 86 | * argument: parameters for property being processed when callback is called 87 | * 88 | * Returns 1 if should continue processing file, 0 if should abort. 89 | * ---------------------------------------------------------------------- */ 90 | typedef int (*p_ply_read_cb)(p_ply_argument argument); 91 | 92 | /* ---------------------------------------------------------------------- 93 | * Sets up callbacks for property reading after header was parsed 94 | * 95 | * ply: handle returned by ply_open 96 | * element_name: element where property is 97 | * property_name: property to associate element with 98 | * read_cb: function to be called for each property value 99 | * pdata/idata: user data that will be passed to callback 100 | * 101 | * Returns 0 if no element or no property in element, returns the 102 | * number of element instances otherwise. 103 | * ---------------------------------------------------------------------- */ 104 | long ply_set_read_cb(p_ply ply, const char *element_name, 105 | const char *property_name, p_ply_read_cb read_cb, 106 | void *pdata, long idata); 107 | 108 | /* ---------------------------------------------------------------------- 109 | * Returns information about the element originating a callback 110 | * 111 | * argument: handle to argument 112 | * element: receives a the element handle (if non-null) 113 | * instance_index: receives the index of the current element instance 114 | * (if non-null) 115 | * 116 | * Returns 1 if successfull, 0 otherwise 117 | * ---------------------------------------------------------------------- */ 118 | int ply_get_argument_element(p_ply_argument argument, 119 | p_ply_element *element, long *instance_index); 120 | 121 | /* ---------------------------------------------------------------------- 122 | * Returns information about the property originating a callback 123 | * 124 | * argument: handle to argument 125 | * property: receives the property handle (if non-null) 126 | * length: receives the number of values in this property (if non-null) 127 | * value_index: receives the index of current property value (if non-null) 128 | * 129 | * Returns 1 if successfull, 0 otherwise 130 | * ---------------------------------------------------------------------- */ 131 | int ply_get_argument_property(p_ply_argument argument, 132 | p_ply_property *property, long *length, long *value_index); 133 | 134 | /* ---------------------------------------------------------------------- 135 | * Returns user data associated with callback 136 | * 137 | * pdata: receives a copy of user custom data pointer (if non-null) 138 | * idata: receives a copy of user custom data integer (if non-null) 139 | * 140 | * Returns 1 if successfull, 0 otherwise 141 | * ---------------------------------------------------------------------- */ 142 | int ply_get_argument_user_data(p_ply_argument argument, void **pdata, 143 | long *idata); 144 | 145 | /* ---------------------------------------------------------------------- 146 | * Returns the value associated with a callback 147 | * 148 | * argument: handle to argument 149 | * 150 | * Returns the current data item 151 | * ---------------------------------------------------------------------- */ 152 | double ply_get_argument_value(p_ply_argument argument); 153 | 154 | /* ---------------------------------------------------------------------- 155 | * Reads all elements and properties calling the callbacks defined with 156 | * calls to ply_set_read_cb 157 | * 158 | * ply: handle returned by ply_open 159 | * 160 | * Returns 1 if successfull, 0 otherwise 161 | * ---------------------------------------------------------------------- */ 162 | int ply_read(p_ply ply); 163 | 164 | /* ---------------------------------------------------------------------- 165 | * Iterates over all elements by returning the next element. 166 | * Call with NULL to return handle to first element. 167 | * 168 | * ply: handle returned by ply_open 169 | * last: handle of last element returned (NULL for first element) 170 | * 171 | * Returns element if successfull or NULL if no more elements 172 | * ---------------------------------------------------------------------- */ 173 | p_ply_element ply_get_next_element(p_ply ply, p_ply_element last); 174 | 175 | /* ---------------------------------------------------------------------- 176 | * Iterates over all comments by returning the next comment. 177 | * Call with NULL to return pointer to first comment. 178 | * 179 | * ply: handle returned by ply_open 180 | * last: pointer to last comment returned (NULL for first comment) 181 | * 182 | * Returns comment if successfull or NULL if no more comments 183 | * ---------------------------------------------------------------------- */ 184 | const char *ply_get_next_comment(p_ply ply, const char *last); 185 | 186 | /* ---------------------------------------------------------------------- 187 | * Iterates over all obj_infos by returning the next obj_info. 188 | * Call with NULL to return pointer to first obj_info. 189 | * 190 | * ply: handle returned by ply_open 191 | * last: pointer to last obj_info returned (NULL for first obj_info) 192 | * 193 | * Returns obj_info if successfull or NULL if no more obj_infos 194 | * ---------------------------------------------------------------------- */ 195 | const char *ply_get_next_obj_info(p_ply ply, const char *last); 196 | 197 | /* ---------------------------------------------------------------------- 198 | * Returns information about an element 199 | * 200 | * element: element of interest 201 | * name: receives a pointer to internal copy of element name (if non-null) 202 | * ninstances: receives the number of instances of this element (if non-null) 203 | * 204 | * Returns 1 if successfull or 0 otherwise 205 | * ---------------------------------------------------------------------- */ 206 | int ply_get_element_info(p_ply_element element, const char** name, 207 | long *ninstances); 208 | 209 | /* ---------------------------------------------------------------------- 210 | * Iterates over all properties by returning the next property. 211 | * Call with NULL to return handle to first property. 212 | * 213 | * element: handle of element with the properties of interest 214 | * last: handle of last property returned (NULL for first property) 215 | * 216 | * Returns element if successfull or NULL if no more properties 217 | * ---------------------------------------------------------------------- */ 218 | p_ply_property ply_get_next_property(p_ply_element element, 219 | p_ply_property last); 220 | 221 | /* ---------------------------------------------------------------------- 222 | * Returns information about a property 223 | * 224 | * property: handle to property of interest 225 | * name: receives a pointer to internal copy of property name (if non-null) 226 | * type: receives the property type (if non-null) 227 | * length_type: for list properties, receives the scalar type of 228 | * the length field (if non-null) 229 | * value_type: for list properties, receives the scalar type of the value 230 | * fields (if non-null) 231 | * 232 | * Returns 1 if successfull or 0 otherwise 233 | * ---------------------------------------------------------------------- */ 234 | int ply_get_property_info(p_ply_property property, const char** name, 235 | e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type); 236 | 237 | /* ---------------------------------------------------------------------- 238 | * Creates new PLY file 239 | * 240 | * name: file name 241 | * storage_mode: file format mode 242 | * error_cb: error callback function 243 | * idata,pdata: contextual information available to users 244 | * 245 | * Returns handle to PLY file if successfull, NULL otherwise 246 | * ---------------------------------------------------------------------- */ 247 | p_ply ply_create(const char *name, e_ply_storage_mode storage_mode, 248 | p_ply_error_cb error_cb, long idata, void *pdata); 249 | 250 | /* ---------------------------------------------------------------------- 251 | * Adds a new element to the PLY file created by ply_create 252 | * 253 | * ply: handle returned by ply_create 254 | * name: name of new element 255 | * ninstances: number of element of this time in file 256 | * 257 | * Returns 1 if successfull, 0 otherwise 258 | * ---------------------------------------------------------------------- */ 259 | int ply_add_element(p_ply ply, const char *name, long ninstances); 260 | 261 | /* ---------------------------------------------------------------------- 262 | * Adds a new property to the last element added by ply_add_element 263 | * 264 | * ply: handle returned by ply_create 265 | * name: name of new property 266 | * type: property type 267 | * length_type: scalar type of length field of a list property 268 | * value_type: scalar type of value fields of a list property 269 | * 270 | * Returns 1 if successfull, 0 otherwise 271 | * ---------------------------------------------------------------------- */ 272 | int ply_add_property(p_ply ply, const char *name, e_ply_type type, 273 | e_ply_type length_type, e_ply_type value_type); 274 | 275 | /* ---------------------------------------------------------------------- 276 | * Adds a new list property to the last element added by ply_add_element 277 | * 278 | * ply: handle returned by ply_create 279 | * name: name of new property 280 | * length_type: scalar type of length field of a list property 281 | * value_type: scalar type of value fields of a list property 282 | * 283 | * Returns 1 if successfull, 0 otherwise 284 | * ---------------------------------------------------------------------- */ 285 | int ply_add_list_property(p_ply ply, const char *name, 286 | e_ply_type length_type, e_ply_type value_type); 287 | 288 | /* ---------------------------------------------------------------------- 289 | * Adds a new property to the last element added by ply_add_element 290 | * 291 | * ply: handle returned by ply_create 292 | * name: name of new property 293 | * type: property type 294 | * 295 | * Returns 1 if successfull, 0 otherwise 296 | * ---------------------------------------------------------------------- */ 297 | int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type); 298 | 299 | /* ---------------------------------------------------------------------- 300 | * Adds a new comment item 301 | * 302 | * ply: handle returned by ply_create 303 | * comment: pointer to string with comment text 304 | * 305 | * Returns 1 if successfull, 0 otherwise 306 | * ---------------------------------------------------------------------- */ 307 | int ply_add_comment(p_ply ply, const char *comment); 308 | 309 | /* ---------------------------------------------------------------------- 310 | * Adds a new obj_info item 311 | * 312 | * ply: handle returned by ply_create 313 | * comment: pointer to string with obj_info data 314 | * 315 | * Returns 1 if successfull, 0 otherwise 316 | * ---------------------------------------------------------------------- */ 317 | int ply_add_obj_info(p_ply ply, const char *obj_info); 318 | 319 | /* ---------------------------------------------------------------------- 320 | * Writes the PLY file header after all element and properties have been 321 | * defined by calls to ply_add_element and ply_add_property 322 | * 323 | * ply: handle returned by ply_create 324 | * 325 | * Returns 1 if successfull, 0 otherwise 326 | * ---------------------------------------------------------------------- */ 327 | int ply_write_header(p_ply ply); 328 | 329 | /* ---------------------------------------------------------------------- 330 | * Writes one property value, in the order they should be written to the 331 | * file. For each element type, write all elements of that type in order. 332 | * For each element, write all its properties in order. For scalar 333 | * properties, just write the value. For list properties, write the length 334 | * and then each of the values. 335 | * 336 | * ply: handle returned by ply_create 337 | * 338 | * Returns 1 if successfull, 0 otherwise 339 | * ---------------------------------------------------------------------- */ 340 | int ply_write(p_ply ply, double value); 341 | 342 | /* ---------------------------------------------------------------------- 343 | * Closes a PLY file handle. Releases all memory used by handle 344 | * 345 | * ply: handle to be closed. 346 | * 347 | * Returns 1 if successfull, 0 otherwise 348 | * ---------------------------------------------------------------------- */ 349 | int ply_close(p_ply ply); 350 | 351 | 352 | /* ---------------------------------------------------------------------- 353 | * Returns storage mode of given ply file 354 | * 355 | * ply: handle to open file 356 | * ---------------------------------------------------------------------- */ 357 | e_ply_storage_mode ply_get_storage_mode(p_ply ply); 358 | 359 | #ifdef __cplusplus 360 | } 361 | #endif 362 | 363 | #endif /* RPLY_H */ 364 | 365 | /* ---------------------------------------------------------------------- 366 | * Copyright (C) 2003-2015 Diego Nehab. All rights reserved. 367 | * 368 | * Permission is hereby granted, free of charge, to any person obtaining 369 | * a copy of this software and associated documentation files (the 370 | * "Software"), to deal in the Software without restriction, including 371 | * without limitation the rights to use, copy, modify, merge, publish, 372 | * distribute, sublicense, and/or sell copies of the Software, and to 373 | * permit persons to whom the Software is furnished to do so, subject to 374 | * the following conditions: 375 | * 376 | * The above copyright notice and this permission notice shall be 377 | * included in all copies or substantial portions of the Software. 378 | * 379 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 380 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 381 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 382 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 383 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 384 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 385 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 386 | * ---------------------------------------------------------------------- */ 387 | -------------------------------------------------------------------------------- /plylibs/rply/rplyfile.h: -------------------------------------------------------------------------------- 1 | #ifndef RPLY_FILE_H 2 | #define RPLY_FILE_H 3 | /* ---------------------------------------------------------------------- 4 | * RPly library, read/write PLY files 5 | * Diego Nehab, IMPA 6 | * http://www.impa.br/~diego/software/rply 7 | * 8 | * This library is distributed under the MIT License. See notice 9 | * at the end of this file. 10 | * ---------------------------------------------------------------------- */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /* ---------------------------------------------------------------------- 17 | * Opens a PLY file for reading (fails if file is not a PLY file) 18 | * 19 | * file_pointer: FILE * to file open for reading 20 | * error_cb: error callback function 21 | * idata,pdata: contextual information available to users 22 | * 23 | * Returns 1 if successful, 0 otherwise 24 | * ---------------------------------------------------------------------- */ 25 | p_ply ply_open_from_file(FILE *file_pointer, p_ply_error_cb error_cb, 26 | long idata, void *pdata); 27 | 28 | /* ---------------------------------------------------------------------- 29 | * Creates new PLY file 30 | * 31 | * file_pointer: FILE * to a file open for writing 32 | * storage_mode: file format mode 33 | * error_cb: error callback function 34 | * idata,pdata: contextual information available to users 35 | * 36 | * Returns handle to PLY file if successfull, NULL otherwise 37 | * ---------------------------------------------------------------------- */ 38 | p_ply ply_create_to_file(FILE *file_pointer, e_ply_storage_mode storage_mode, 39 | p_ply_error_cb error_cb, long idata, void *pdata); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif /* RPLY_FILE_H */ 46 | 47 | /* ---------------------------------------------------------------------- 48 | * Copyright (C) 2003-2015 Diego Nehab. All rights reserved. 49 | * 50 | * Permission is hereby granted, free of charge, to any person obtaining 51 | * a copy of this software and associated documentation files (the 52 | * "Software"), to deal in the Software without restriction, including 53 | * without limitation the rights to use, copy, modify, merge, publish, 54 | * distribute, sublicense, and/or sell copies of the Software, and to 55 | * permit persons to whom the Software is furnished to do so, subject to 56 | * the following conditions: 57 | * 58 | * The above copyright notice and this permission notice shall be 59 | * included in all copies or substantial portions of the Software. 60 | * 61 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 62 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 63 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 64 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 65 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 66 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 67 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 68 | * ---------------------------------------------------------------------- */ 69 | -------------------------------------------------------------------------------- /plylibs/tinyply21/tinyply.cpp: -------------------------------------------------------------------------------- 1 | // This software is in the public domain. Where that dedication is not 2 | // recognized, you are granted a perpetual, irrevocable license to copy, 3 | // distribute, and modify this file as you see fit. 4 | // Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com) 5 | // https://github.com/ddiakopoulos/tinyply 6 | // Version 2.1 7 | 8 | #include "tinyply.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace tinyply; 16 | using namespace std; 17 | 18 | ////////////////// 19 | // Endian Utils // 20 | ////////////////// 21 | 22 | template T endian_swap(const T & v) { return v; } 23 | template<> inline uint16_t endian_swap(const uint16_t & v) { return (v << 8) | (v >> 8); } 24 | template<> inline uint32_t endian_swap(const uint32_t & v) { return (v << 24) | ((v << 8) & 0x00ff0000) | ((v >> 8) & 0x0000ff00) | (v >> 24); } 25 | template<> inline uint64_t endian_swap(const uint64_t & v) 26 | { 27 | return (((v & 0x00000000000000ffLL) << 56) | 28 | ((v & 0x000000000000ff00LL) << 40) | 29 | ((v & 0x0000000000ff0000LL) << 24) | 30 | ((v & 0x00000000ff000000LL) << 8) | 31 | ((v & 0x000000ff00000000LL) >> 8) | 32 | ((v & 0x0000ff0000000000LL) >> 24) | 33 | ((v & 0x00ff000000000000LL) >> 40) | 34 | ((v & 0xff00000000000000LL) >> 56)); 35 | } 36 | template<> inline int16_t endian_swap(const int16_t & v) { uint16_t r = endian_swap(*(uint16_t*)&v); return *(int16_t*)&r; } 37 | template<> inline int32_t endian_swap(const int32_t & v) { uint32_t r = endian_swap(*(uint32_t*)&v); return *(int32_t*)&r; } 38 | template<> inline int64_t endian_swap(const int64_t & v) { uint64_t r = endian_swap(*(uint64_t*)&v); return *(int64_t*)&r; } 39 | inline float endian_swap_float(const uint32_t & v) { union { float f; uint32_t i; }; i = endian_swap(v); return f; } 40 | inline double endian_swap_double(const uint64_t & v) { union { double d; uint64_t i; }; i = endian_swap(v); return d; } 41 | 42 | ///////////////////////////// 43 | // Internal Implementation // 44 | ///////////////////////////// 45 | 46 | inline uint32_t hash_fnv1a(const std::string & str) 47 | { 48 | static const uint32_t fnv1aBase32 = 0x811C9DC5u; 49 | static const uint32_t fnv1aPrime32 = 0x01000193u; 50 | 51 | uint32_t result = fnv1aBase32; 52 | 53 | for (auto & c : str) 54 | { 55 | result ^= static_cast(c); 56 | result *= fnv1aPrime32; 57 | } 58 | return result; 59 | } 60 | 61 | inline Type property_type_from_string(const std::string & t) 62 | { 63 | if (t == "int8" || t == "char") return Type::INT8; 64 | else if (t == "uint8" || t == "uchar") return Type::UINT8; 65 | else if (t == "int16" || t == "short") return Type::INT16; 66 | else if (t == "uint16" || t == "ushort") return Type::UINT16; 67 | else if (t == "int32" || t == "int") return Type::INT32; 68 | else if (t == "uint32" || t == "uint") return Type::UINT32; 69 | else if (t == "float32" || t == "float") return Type::FLOAT32; 70 | else if (t == "float64" || t == "double") return Type::FLOAT64; 71 | return Type::INVALID; 72 | } 73 | 74 | struct PlyFile::PlyFileImpl 75 | { 76 | struct PlyCursor 77 | { 78 | size_t byteOffset; 79 | size_t totalSizeBytes; 80 | }; 81 | 82 | struct ParsingHelper 83 | { 84 | std::shared_ptr data; 85 | std::shared_ptr cursor; 86 | }; 87 | 88 | std::unordered_map userData; 89 | 90 | bool isBinary = false; 91 | bool isBigEndian = false; 92 | std::vector elements; 93 | std::vector comments; 94 | std::vector objInfo; 95 | uint8_t scratch[128]; 96 | 97 | void read(std::istream & is, uint32_t fixed_list_size); 98 | void write(std::ostream & os, bool isBinary); 99 | 100 | std::shared_ptr request_properties_from_element(const std::string & elementKey, const std::initializer_list propertyKeys); 101 | void add_properties_to_element(const std::string & elementKey, const std::initializer_list propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount); 102 | 103 | size_t read_property_binary(const Type t, void * dest, size_t & destOffset, std::istream & is); 104 | size_t read_property_ascii(const Type t, void * dest, size_t & destOffset, std::istream & is); 105 | size_t skip_property_binary(const PlyProperty & property, std::istream & is); 106 | size_t skip_property_ascii(const PlyProperty & property, std::istream & is); 107 | 108 | bool parse_header(std::istream & is); 109 | void parse_data(std::istream & is, bool firstPass); 110 | void read_header_format(std::istream & is); 111 | void read_header_element(std::istream & is); 112 | void read_header_property(std::istream & is); 113 | void read_header_text(std::string line, std::istream & is, std::vector & place, int erase = 0); 114 | 115 | void write_header(std::ostream & os); 116 | void write_ascii_internal(std::ostream & os); 117 | void write_binary_internal(std::ostream & os); 118 | void write_property_ascii(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset); 119 | void write_property_binary(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset); 120 | }; 121 | 122 | ////////////////// 123 | // PLY Property // 124 | ////////////////// 125 | 126 | PlyProperty::PlyProperty(std::istream & is) : isList(false) 127 | { 128 | std::string type; 129 | is >> type; 130 | if (type == "list") 131 | { 132 | std::string countType; 133 | is >> countType >> type; 134 | listType = property_type_from_string(countType); 135 | isList = true; 136 | } 137 | propertyType = property_type_from_string(type); 138 | is >> name; 139 | } 140 | 141 | ///////////////// 142 | // PLY Element // 143 | ///////////////// 144 | 145 | PlyElement::PlyElement(std::istream & is) 146 | { 147 | is >> name >> size; 148 | } 149 | 150 | /////////// 151 | // Utils // 152 | /////////// 153 | 154 | template void ply_cast(void * dest, const char * src, bool be) 155 | { 156 | *(static_cast(dest)) = (be) ? endian_swap(*(reinterpret_cast(src))) : *(reinterpret_cast(src)); 157 | } 158 | 159 | template void ply_cast_float(void * dest, const char * src, bool be) 160 | { 161 | *(static_cast(dest)) = (be) ? endian_swap_float(*(reinterpret_cast(src))) : *(reinterpret_cast(src)); 162 | } 163 | 164 | template void ply_cast_double(void * dest, const char * src, bool be) 165 | { 166 | *(static_cast(dest)) = (be) ? endian_swap_double(*(reinterpret_cast(src))) : *(reinterpret_cast(src)); 167 | } 168 | 169 | template T ply_read_ascii(std::istream & is) 170 | { 171 | T data; 172 | is >> data; 173 | return data; 174 | } 175 | 176 | template void ply_cast_ascii(void * dest, std::istream & is) 177 | { 178 | *(static_cast(dest)) = ply_read_ascii(is); 179 | } 180 | 181 | int64_t find_element(const std::string & key, const std::vector & list) 182 | { 183 | for (size_t i = 0; i < list.size(); i++) if (list[i].name == key) return i; 184 | return -1; 185 | } 186 | 187 | int64_t find_property(const std::string & key, const std::vector & list) 188 | { 189 | for (size_t i = 0; i < list.size(); ++i) if (list[i].name == key) return i; 190 | return -1; 191 | } 192 | 193 | ////////////// 194 | // PLY File // 195 | ////////////// 196 | 197 | bool PlyFile::PlyFileImpl::parse_header(std::istream & is) 198 | { 199 | std::string line; 200 | while (std::getline(is, line)) 201 | { 202 | std::istringstream ls(line); 203 | std::string token; 204 | ls >> token; 205 | if (token == "ply" || token == "PLY" || token == "") continue; 206 | else if (token == "comment") read_header_text(line, ls, comments, 8); 207 | else if (token == "format") read_header_format(ls); 208 | else if (token == "element") read_header_element(ls); 209 | else if (token == "property") read_header_property(ls); 210 | else if (token == "obj_info") read_header_text(line, ls, objInfo, 9); 211 | else if (token == "end_header") break; 212 | else return false; // unexpected header field 213 | } 214 | return true; 215 | } 216 | 217 | void PlyFile::PlyFileImpl::read_header_text(std::string line, std::istream & is, std::vector& place, int erase) 218 | { 219 | place.push_back((erase > 0) ? line.erase(0, erase) : line); 220 | } 221 | 222 | void PlyFile::PlyFileImpl::read_header_format(std::istream & is) 223 | { 224 | std::string s; 225 | (is >> s); 226 | if (s == "binary_little_endian") isBinary = true; 227 | else if (s == "binary_big_endian") isBinary = isBigEndian = true; 228 | } 229 | 230 | void PlyFile::PlyFileImpl::read_header_element(std::istream & is) 231 | { 232 | elements.emplace_back(is); 233 | } 234 | 235 | void PlyFile::PlyFileImpl::read_header_property(std::istream & is) 236 | { 237 | if (!elements.size()) throw std::runtime_error("no elements defined; file is malformed"); 238 | elements.back().properties.emplace_back(is); 239 | } 240 | 241 | size_t PlyFile::PlyFileImpl::skip_property_binary(const PlyProperty & p, std::istream & is) 242 | { 243 | const int32_t stride = PropertyTable[p.propertyType].stride; 244 | 245 | if (p.isList) 246 | { 247 | size_t listSize = 0; 248 | size_t dummyCount = 0; 249 | read_property_binary(p.listType, &listSize, dummyCount, is); 250 | for (size_t i = 0; i < listSize; ++i) is.read((char*) scratch, stride); 251 | return listSize * stride; // in bytes 252 | } 253 | else 254 | { 255 | is.read((char*)scratch, PropertyTable[p.propertyType].stride); 256 | return stride; 257 | } 258 | } 259 | 260 | size_t PlyFile::PlyFileImpl::skip_property_ascii(const PlyProperty & p, std::istream & is) 261 | { 262 | const int32_t stride = PropertyTable[p.propertyType].stride; 263 | 264 | std::string skip; 265 | if (p.isList) 266 | { 267 | size_t listSize = 0; 268 | size_t dummyCount = 0; 269 | read_property_ascii(p.listType, &listSize, dummyCount, is); 270 | for (size_t i = 0; i < listSize; ++i) is >> skip; 271 | return listSize * stride; // in bytes 272 | } 273 | else 274 | { 275 | is >> skip; 276 | return stride; 277 | } 278 | } 279 | 280 | size_t PlyFile::PlyFileImpl::read_property_binary(const Type t, void * dest, size_t & destOffset, std::istream & is) 281 | { 282 | const int32_t stride = PropertyTable[t].stride; 283 | destOffset += stride; 284 | is.read((char*) scratch, stride); 285 | 286 | switch (t) 287 | { 288 | case Type::INT8: ply_cast(dest, (char*)scratch, isBigEndian); break; 289 | case Type::UINT8: ply_cast(dest, (char*)scratch, isBigEndian); break; 290 | case Type::INT16: ply_cast(dest, (char*)scratch, isBigEndian); break; 291 | case Type::UINT16: ply_cast(dest, (char*)scratch, isBigEndian); break; 292 | case Type::INT32: ply_cast(dest, (char*)scratch, isBigEndian); break; 293 | case Type::UINT32: ply_cast(dest, (char*)scratch, isBigEndian); break; 294 | case Type::FLOAT32: ply_cast_float(dest, (char*)scratch, isBigEndian); break; 295 | case Type::FLOAT64: ply_cast_double(dest, (char*)scratch, isBigEndian); break; 296 | case Type::INVALID: throw std::invalid_argument("invalid ply property"); 297 | } 298 | 299 | return stride; 300 | } 301 | 302 | size_t PlyFile::PlyFileImpl::read_property_ascii(const Type t, void * dest, size_t & destOffset, std::istream & is) 303 | { 304 | const int32_t stride = PropertyTable[t].stride; 305 | destOffset += stride; 306 | 307 | switch (t) 308 | { 309 | case Type::INT8: *((int8_t *)dest) = ply_read_ascii(is); break; 310 | case Type::UINT8: *((uint8_t *)dest) = ply_read_ascii(is); break; 311 | case Type::INT16: ply_cast_ascii(dest, is); break; 312 | case Type::UINT16: ply_cast_ascii(dest, is); break; 313 | case Type::INT32: ply_cast_ascii(dest, is); break; 314 | case Type::UINT32: ply_cast_ascii(dest, is); break; 315 | case Type::FLOAT32: ply_cast_ascii(dest, is); break; 316 | case Type::FLOAT64: ply_cast_ascii(dest, is); break; 317 | case Type::INVALID: throw std::invalid_argument("invalid ply property"); 318 | } 319 | return stride; 320 | } 321 | 322 | void PlyFile::PlyFileImpl::write_property_ascii(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset) 323 | { 324 | switch (t) 325 | { 326 | case Type::INT8: os << static_cast(*reinterpret_cast(src)); break; 327 | case Type::UINT8: os << static_cast(*reinterpret_cast(src)); break; 328 | case Type::INT16: os << *reinterpret_cast(src); break; 329 | case Type::UINT16: os << *reinterpret_cast(src); break; 330 | case Type::INT32: os << *reinterpret_cast(src); break; 331 | case Type::UINT32: os << *reinterpret_cast(src); break; 332 | case Type::FLOAT32: os << *reinterpret_cast(src); break; 333 | case Type::FLOAT64: os << *reinterpret_cast(src); break; 334 | case Type::INVALID: throw std::invalid_argument("invalid ply property"); 335 | } 336 | os << " "; 337 | srcOffset += PropertyTable[t].stride; 338 | } 339 | 340 | void PlyFile::PlyFileImpl::write_property_binary(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset) 341 | { 342 | os.write((char *)src, PropertyTable[t].stride); 343 | srcOffset += PropertyTable[t].stride; 344 | } 345 | 346 | void PlyFile::PlyFileImpl::read(std::istream & is, uint32_t fixed_list_size) 347 | { 348 | // Parse but only get the data size 349 | if (fixed_list_size == 0) parse_data(is, true); 350 | 351 | std::vector> buffers; 352 | for (auto & entry : userData) buffers.push_back(entry.second.data); 353 | 354 | // Count the number of properties (required for allocation) 355 | std::unordered_map uniqueCount; 356 | for (auto & ptr : buffers) uniqueCount[ptr.get()] += 1; 357 | 358 | // Since group-requested properties share the same cursor, we need to find unique cursors so we only allocate once 359 | std::sort(buffers.begin(), buffers.end()); 360 | buffers.erase(std::unique(buffers.begin(), buffers.end()), buffers.end()); 361 | 362 | // Not great, but since we sorted by ptrs on PlyData, need to remap back onto its cursor in the userData table 363 | for (auto & b : buffers) 364 | { 365 | for (auto & entry : userData) 366 | { 367 | if (entry.second.data == b && b->buffer.get() == nullptr) 368 | { 369 | // A fixed_list_size of 0 means we have executed two passes over the file 370 | // to compute the total length of all (potentially) variable-length lists 371 | if (fixed_list_size == 0) 372 | { 373 | b->buffer = Buffer(entry.second.cursor->totalSizeBytes); 374 | } 375 | else 376 | { 377 | // otherwise, we can allocate up front and skip the first pass for extra speed. 378 | auto bytes = entry.second.data->count * PropertyTable[entry.second.data->t].stride * (entry.second.data->isList ? fixed_list_size : 1); 379 | bytes *= uniqueCount[b.get()]; 380 | 381 | b->buffer = Buffer(bytes); 382 | } 383 | 384 | } 385 | } 386 | } 387 | 388 | // Populate the data 389 | parse_data(is, false); 390 | } 391 | 392 | void PlyFile::PlyFileImpl::write(std::ostream & os, bool _isBinary) 393 | { 394 | if (_isBinary) write_binary_internal(os); 395 | else write_ascii_internal(os); 396 | } 397 | 398 | void PlyFile::PlyFileImpl::write_binary_internal(std::ostream & os) 399 | { 400 | isBinary = true; 401 | write_header(os); 402 | uint8_t listSize[4] = { 0, 0, 0, 0 }; 403 | size_t dummyCount = 0; 404 | for (auto & e : elements) 405 | { 406 | for (size_t i = 0; i < e.size; ++i) 407 | { 408 | for (auto & p : e.properties) 409 | { 410 | auto & helper = userData[hash_fnv1a(e.name + p.name)]; 411 | if (p.isList) 412 | { 413 | std::memcpy(listSize, &p.listCount, sizeof(uint32_t)); 414 | write_property_binary(p.listType, os, listSize, dummyCount); 415 | for (int j = 0; j < p.listCount; ++j) 416 | { 417 | write_property_binary(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset); 418 | } 419 | } 420 | else 421 | { 422 | write_property_binary(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset); 423 | } 424 | } 425 | } 426 | } 427 | } 428 | 429 | void PlyFile::PlyFileImpl::write_ascii_internal(std::ostream & os) 430 | { 431 | write_header(os); 432 | 433 | for (auto & e : elements) 434 | { 435 | for (size_t i = 0; i < e.size; ++i) 436 | { 437 | for (auto & p : e.properties) 438 | { 439 | auto & helper = userData[hash_fnv1a(e.name + p.name)]; 440 | if (p.isList) 441 | { 442 | os << p.listCount << " "; 443 | for (int j = 0; j < p.listCount; ++j) 444 | { 445 | write_property_ascii(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset); 446 | } 447 | } 448 | else 449 | { 450 | write_property_ascii(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset); 451 | } 452 | } 453 | os << "\n"; 454 | } 455 | } 456 | } 457 | 458 | void PlyFile::PlyFileImpl::write_header(std::ostream & os) 459 | { 460 | const std::locale & fixLoc = std::locale("C"); 461 | os.imbue(fixLoc); 462 | 463 | os << "ply\n"; 464 | if (isBinary) os << ((isBigEndian) ? "format binary_big_endian 1.0" : "format binary_little_endian 1.0") << "\n"; 465 | else os << "format ascii 1.0\n"; 466 | 467 | for (const auto & comment : comments) os << "comment " << comment << "\n"; 468 | 469 | for (auto & e : elements) 470 | { 471 | os << "element " << e.name << " " << e.size << "\n"; 472 | for (const auto & p : e.properties) 473 | { 474 | if (p.isList) 475 | { 476 | os << "property list " << PropertyTable[p.listType].str << " " 477 | << PropertyTable[p.propertyType].str << " " << p.name << "\n"; 478 | } 479 | else 480 | { 481 | os << "property " << PropertyTable[p.propertyType].str << " " << p.name << "\n"; 482 | } 483 | } 484 | } 485 | os << "end_header\n"; 486 | } 487 | 488 | std::shared_ptr PlyFile::PlyFileImpl::request_properties_from_element(const std::string & elementKey, const std::initializer_list propertyKeys) 489 | { 490 | // All requested properties in the userDataTable share the same cursor (thrown into the same flat array) 491 | ParsingHelper helper; 492 | helper.data = std::make_shared(); 493 | helper.data->count = 0; 494 | helper.data->isList = false; 495 | helper.data->t = Type::INVALID; 496 | helper.cursor = std::make_shared(); 497 | helper.cursor->byteOffset = 0; 498 | helper.cursor->totalSizeBytes = 0; 499 | 500 | if (elements.size() == 0) throw std::runtime_error("parsed header had no elements defined. malformed file?"); 501 | if (!propertyKeys.size()) throw std::invalid_argument("`propertyKeys` argument is empty"); 502 | if (elementKey.size() == 0) throw std::invalid_argument("`elementKey` argument is empty"); 503 | 504 | const int64_t elementIndex = find_element(elementKey, elements); 505 | 506 | // Sanity check if the user requested element is in the pre-parsed header 507 | if (elementIndex >= 0) 508 | { 509 | // We found the element 510 | const PlyElement & element = elements[elementIndex]; 511 | 512 | helper.data->count = element.size; 513 | 514 | // Find each of the keys 515 | for (auto key : propertyKeys) 516 | { 517 | const int64_t propertyIndex = find_property(key, element.properties); 518 | if (propertyIndex >= 0) 519 | { 520 | // We found the property 521 | const PlyProperty & property = element.properties[propertyIndex]; 522 | helper.data->t = property.propertyType; 523 | helper.data->isList = property.isList; 524 | auto result = userData.insert(std::pair(hash_fnv1a(element.name + property.name), helper)); 525 | if (result.second == false) 526 | { 527 | throw std::invalid_argument("element-property key has already been requested: " + hash_fnv1a(element.name + property.name)); 528 | } 529 | } 530 | else throw std::invalid_argument("one of the property keys was not found in the header: " + key); 531 | } 532 | } 533 | else throw std::invalid_argument("the element key was not found in the header: " + elementKey); 534 | 535 | return helper.data; 536 | } 537 | 538 | void PlyFile::PlyFileImpl::add_properties_to_element(const std::string & elementKey, const std::initializer_list propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount) 539 | { 540 | ParsingHelper helper; 541 | helper.data = std::make_shared(); 542 | helper.data->count = count; 543 | helper.data->t = type; 544 | helper.data->buffer = Buffer(data); 545 | helper.cursor = std::make_shared(); 546 | helper.cursor->byteOffset = 0; 547 | helper.cursor->totalSizeBytes = 0; 548 | 549 | auto create_property_on_element = [&](PlyElement & e) 550 | { 551 | for (auto key : propertyKeys) 552 | { 553 | PlyProperty newProp = (listType == Type::INVALID) ? PlyProperty(type, key) : PlyProperty(listType, type, key, listCount); 554 | userData.insert(std::pair(hash_fnv1a(elementKey + key), helper)); 555 | e.properties.push_back(newProp); 556 | } 557 | }; 558 | 559 | int64_t idx = find_element(elementKey, elements); 560 | if (idx >= 0) 561 | { 562 | PlyElement & e = elements[idx]; 563 | create_property_on_element(e); 564 | } 565 | else 566 | { 567 | PlyElement newElement = (listType == Type::INVALID) ? PlyElement(elementKey, count) : PlyElement(elementKey, count); 568 | create_property_on_element(newElement); 569 | elements.push_back(newElement); 570 | } 571 | } 572 | 573 | void PlyFile::PlyFileImpl::parse_data(std::istream & is, bool firstPass) 574 | { 575 | std::function read; 576 | std::function skip; 577 | 578 | const auto start = is.tellg(); 579 | 580 | if (isBinary) 581 | { 582 | read = [this](const Type t, void * dest, size_t & destOffset, std::istream & _is) { return read_property_binary(t, dest, destOffset, _is); }; 583 | skip = [this](const PlyProperty & p, std::istream & _is) { return skip_property_binary(p, _is); }; 584 | } 585 | else 586 | { 587 | read = [this](const Type t, void * dest, size_t & destOffset, std::istream & _is) { return read_property_ascii(t, dest, destOffset, _is); }; 588 | skip = [this](const PlyProperty & p, std::istream & _is) { return skip_property_ascii(p, _is); }; 589 | } 590 | 591 | for (auto & element : elements) 592 | { 593 | for (size_t count = 0; count < element.size; ++count) 594 | { 595 | for (auto & property : element.properties) 596 | { 597 | auto cursorIt = userData.find(hash_fnv1a(element.name + property.name)); 598 | if (cursorIt != userData.end()) 599 | { 600 | auto & helper = cursorIt->second; 601 | if (!firstPass) 602 | { 603 | if (property.isList) 604 | { 605 | size_t listSize = 0; 606 | size_t dummyCount = 0; 607 | read(property.listType, &listSize, dummyCount, is); 608 | for (size_t i = 0; i < listSize; ++i) 609 | { 610 | read(property.propertyType, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset, is); 611 | } 612 | } 613 | else 614 | { 615 | read(property.propertyType, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset, is); 616 | } 617 | } 618 | else 619 | { 620 | helper.cursor->totalSizeBytes += skip(property, is); 621 | } 622 | } 623 | else 624 | { 625 | skip(property, is); 626 | } 627 | } 628 | } 629 | } 630 | 631 | // Reset istream reader to the beginning 632 | if (firstPass) is.seekg(start, is.beg); 633 | } 634 | 635 | /////////////////////////////////// 636 | // Passthrough Public Interface // 637 | /////////////////////////////////// 638 | 639 | PlyFile::PlyFile() { impl.reset(new PlyFileImpl()); }; 640 | PlyFile::~PlyFile() { }; 641 | bool PlyFile::is_binary_file() const { return impl->isBinary; } 642 | bool PlyFile::parse_header(std::istream & is) { return impl->parse_header(is); } 643 | void PlyFile::read(std::istream & is, uint32_t fixed_list_size) { return impl->read(is, fixed_list_size); } 644 | void PlyFile::write(std::ostream & os, bool isBinary) { return impl->write(os, isBinary); } 645 | std::vector PlyFile::get_elements() const { return impl->elements; } 646 | std::vector & PlyFile::get_comments() { return impl->comments; } 647 | std::vector PlyFile::get_info() const { return impl->objInfo; } 648 | std::shared_ptr PlyFile::request_properties_from_element(const std::string & elementKey, const std::initializer_list propertyKeys) 649 | { 650 | return impl->request_properties_from_element(elementKey, propertyKeys); 651 | } 652 | void PlyFile::add_properties_to_element(const std::string & elementKey, const std::initializer_list propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount) 653 | { 654 | return impl->add_properties_to_element(elementKey, propertyKeys, type, count, data, listType, listCount); 655 | } -------------------------------------------------------------------------------- /plylibs/tinyply21/tinyply.h: -------------------------------------------------------------------------------- 1 | // This software is in the public domain. Where that dedication is not 2 | // recognized, you are granted a perpetual, irrevocable license to copy, 3 | // distribute, and modify this file as you see fit. 4 | // Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com) 5 | // https://github.com/ddiakopoulos/tinyply 6 | // Version 2.1 7 | 8 | #ifndef tinyply_h 9 | #define tinyply_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace tinyply 20 | { 21 | 22 | enum class Type : uint8_t 23 | { 24 | INVALID, 25 | INT8, 26 | UINT8, 27 | INT16, 28 | UINT16, 29 | INT32, 30 | UINT32, 31 | FLOAT32, 32 | FLOAT64 33 | }; 34 | 35 | struct PropertyInfo 36 | { 37 | int stride; 38 | std::string str; 39 | }; 40 | 41 | static std::map PropertyTable 42 | { 43 | { Type::INT8,{ 1, "char" } }, 44 | { Type::UINT8,{ 1, "uchar" } }, 45 | { Type::INT16,{ 2, "short" } }, 46 | { Type::UINT16,{ 2, "ushort" } }, 47 | { Type::INT32,{ 4, "int" } }, 48 | { Type::UINT32,{ 4, "uint" } }, 49 | { Type::FLOAT32,{ 4, "float" } }, 50 | { Type::FLOAT64,{ 8, "double" } }, 51 | { Type::INVALID,{ 0, "INVALID" } } 52 | }; 53 | 54 | class Buffer 55 | { 56 | uint8_t * alias{ nullptr }; 57 | struct delete_array { void operator()(uint8_t * p) { delete[] p; } }; 58 | std::unique_ptr data; 59 | size_t size; 60 | public: 61 | Buffer() {}; 62 | Buffer(const size_t size) : data(new uint8_t[size], delete_array()), size(size) { alias = data.get(); } // allocating 63 | Buffer(uint8_t * ptr) { alias = ptr; } // non-allocating, todo: set size? 64 | uint8_t * get() { return alias; } 65 | size_t size_bytes() const { return size; } 66 | }; 67 | 68 | struct PlyData 69 | { 70 | Type t; 71 | size_t count; 72 | Buffer buffer; 73 | bool isList; 74 | }; 75 | 76 | struct PlyProperty 77 | { 78 | PlyProperty(std::istream & is); 79 | PlyProperty(Type type, std::string & _name) : name(_name), propertyType(type) {} 80 | PlyProperty(Type list_type, Type prop_type, std::string & _name, size_t list_count) 81 | : name(_name), propertyType(prop_type), isList(true), listType(list_type), listCount(list_count) {} 82 | std::string name; 83 | Type propertyType; 84 | bool isList{ false }; 85 | Type listType{ Type::INVALID }; 86 | size_t listCount{ 0 }; 87 | }; 88 | 89 | struct PlyElement 90 | { 91 | PlyElement(std::istream & istream); 92 | PlyElement(const std::string & _name, size_t count) : name(_name), size(count) {} 93 | std::string name; 94 | size_t size; 95 | std::vector properties; 96 | }; 97 | 98 | struct PlyFile 99 | { 100 | struct PlyFileImpl; 101 | std::unique_ptr impl; 102 | 103 | PlyFile(); 104 | ~PlyFile(); 105 | 106 | /* 107 | * The ply format requires an ascii header. This can be used to determine at 108 | * runtime which properties or elements exist in the file. Limited validation of the 109 | * header is performed; it is assumed the header correctly reflects the contents of the 110 | * payload. This function may throw. Returns true on success, false on failure. 111 | */ 112 | bool parse_header(std::istream & is); 113 | 114 | /* 115 | * In the general case where |fixed_list_size| is zero, `read` performs a two-pass 116 | * parse to support variable length lists. The first pass determines the size of memory 117 | * to allocate, while the second pass performs the read. The most general use of the 118 | * ply format is storing triangle meshes. When this fact is known a-priori, we can pass 119 | * an expected list length that will apply to *all* list elements in a file (e.g. 3). Doing 120 | * so results in an up-front memory allocation and a single-pass import, generally a ~2x speedup. 121 | */ 122 | void read(std::istream & is, uint32_t fixed_list_size = 0); 123 | 124 | /* 125 | * `write` performs no validation and assumes that the data passed into 126 | * `add_properties_to_element` is well-formed. 127 | */ 128 | void write(std::ostream & os, bool isBinary); 129 | bool is_binary_file() const; 130 | 131 | std::vector get_elements() const; 132 | std::vector get_info() const; 133 | std::vector & get_comments(); 134 | 135 | std::shared_ptr request_properties_from_element(const std::string & elementKey, 136 | const std::initializer_list propertyKeys); 137 | 138 | void add_properties_to_element(const std::string & elementKey, 139 | const std::initializer_list propertyKeys, 140 | const Type type, 141 | const size_t count, 142 | uint8_t * data, 143 | const Type listType, 144 | const size_t listCount); 145 | }; 146 | 147 | } // namespace tinyply 148 | 149 | #endif // tinyply_h -------------------------------------------------------------------------------- /plylibs/tinyply23/tinyply.cpp: -------------------------------------------------------------------------------- 1 | // This file exists to create a nice static or shared library via cmake 2 | // but can otherwise be omitted if you prefer to compile tinyply 3 | // directly into your own project. 4 | #define TINYPLY_IMPLEMENTATION 5 | #include "tinyply.h" -------------------------------------------------------------------------------- /plylibs/turkply/ply_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Header for PLY polygon files. 4 | 5 | - Greg Turk 6 | 7 | A PLY file contains a single polygonal _object_. 8 | 9 | An object is composed of lists of _elements_. Typical elements are 10 | vertices, faces, edges and materials. 11 | 12 | Each type of element for a given object has one or more _properties_ 13 | associated with the element type. For instance, a vertex element may 14 | have as properties three floating-point values x,y,z and three unsigned 15 | chars for red, green and blue. 16 | 17 | ----------------------------------------------------------------------- 18 | 19 | Copyright (c) 1998 Georgia Institute of Technology. All rights reserved. 20 | 21 | Permission to use, copy, modify and distribute this software and its 22 | documentation for any purpose is hereby granted without fee, provided 23 | that the above copyright notice and this permission notice appear in 24 | all copies of this software and that you do not sell the software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, 27 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 28 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 29 | 30 | */ 31 | 32 | #ifndef __PLY_H__ 33 | #define __PLY_H__ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #include 40 | #include 41 | 42 | #define PLY_ASCII 1 /* ascii PLY file */ 43 | #define PLY_BINARY_BE 2 /* binary PLY file, big endian */ 44 | #define PLY_BINARY_LE 3 /* binary PLY file, little endian */ 45 | 46 | #define PLY_OKAY 0 /* ply routine worked okay */ 47 | #define PLY_ERROR -1 /* error in ply routine */ 48 | 49 | /* scalar data types supported by PLY format */ 50 | 51 | #define StartType 0 52 | #define Int8 1 53 | #define Int16 2 54 | #define Int32 3 55 | #define Uint8 4 56 | #define Uint16 5 57 | #define Uint32 6 58 | #define Float32 7 59 | #define Float64 8 60 | #define EndType 9 61 | 62 | #define PLY_SCALAR 0 63 | #define PLY_LIST 1 64 | #define PLY_STRING 2 65 | 66 | 67 | typedef struct PlyProperty { /* description of a property */ 68 | 69 | char *name; /* property name */ 70 | int external_type; /* file's data type */ 71 | int internal_type; /* program's data type */ 72 | int offset; /* offset bytes of prop in a struct */ 73 | 74 | int is_list; /* 0 = scalar, 1 = list, 2 = char string */ 75 | int count_external; /* file's count type */ 76 | int count_internal; /* program's count type */ 77 | int count_offset; /* offset byte for list count */ 78 | 79 | } PlyProperty; 80 | 81 | typedef struct PlyElement { /* description of an element */ 82 | char *name; /* element name */ 83 | int num; /* number of elements in this object */ 84 | int size; /* size of element (bytes) or -1 if variable */ 85 | int nprops; /* number of properties for this element */ 86 | PlyProperty **props; /* list of properties in the file */ 87 | char *store_prop; /* flags: property wanted by user? */ 88 | int other_offset; /* offset to un-asked-for props, or -1 if none*/ 89 | int other_size; /* size of other_props structure */ 90 | } PlyElement; 91 | 92 | typedef struct PlyOtherProp { /* describes other properties in an element */ 93 | char *name; /* element name */ 94 | int size; /* size of other_props */ 95 | int nprops; /* number of properties in other_props */ 96 | PlyProperty **props; /* list of properties in other_props */ 97 | } PlyOtherProp; 98 | 99 | typedef struct OtherData { /* for storing other_props for an other element */ 100 | void *other_props; 101 | } OtherData; 102 | 103 | typedef struct OtherElem { /* data for one "other" element */ 104 | char *elem_name; /* names of other elements */ 105 | int elem_count; /* count of instances of each element */ 106 | OtherData **other_data; /* actual property data for the elements */ 107 | PlyOtherProp *other_props; /* description of the property data */ 108 | } OtherElem; 109 | 110 | typedef struct PlyOtherElems { /* "other" elements, not interpreted by user */ 111 | int num_elems; /* number of other elements */ 112 | OtherElem *other_list; /* list of data for other elements */ 113 | } PlyOtherElems; 114 | 115 | #define AVERAGE_RULE 1 116 | #define MAJORITY_RULE 2 117 | #define MINIMUM_RULE 3 118 | #define MAXIMUM_RULE 4 119 | #define SAME_RULE 5 120 | #define RANDOM_RULE 6 121 | 122 | typedef struct PlyPropRules { /* rules for combining "other" properties */ 123 | PlyElement *elem; /* element whose rules we are making */ 124 | int *rule_list; /* types of rules (AVERAGE_PLY, MAJORITY_PLY, etc.) */ 125 | int nprops; /* number of properties we're combining so far */ 126 | int max_props; /* maximum number of properties we have room for now */ 127 | void **props; /* list of properties we're combining */ 128 | float *weights; /* list of weights of the properties */ 129 | } PlyPropRules; 130 | 131 | typedef struct PlyRuleList { 132 | char *name; /* name of the rule */ 133 | char *element; /* name of element that rule applies to */ 134 | char *property; /* name of property that rule applies to */ 135 | struct PlyRuleList *next; /* pointer for linked list of rules */ 136 | } PlyRuleList; 137 | 138 | typedef struct PlyFile { /* description of PLY file */ 139 | FILE *fp; /* file pointer */ 140 | int file_type; /* ascii or binary */ 141 | float version; /* version number of file */ 142 | int num_elem_types; /* number of element types of object */ 143 | PlyElement **elems; /* list of elements */ 144 | int num_comments; /* number of comments */ 145 | char **comments; /* list of comments */ 146 | int num_obj_info; /* number of items of object information */ 147 | char **obj_info; /* list of object info items */ 148 | PlyElement *which_elem; /* element we're currently reading or writing */ 149 | PlyOtherElems *other_elems; /* "other" elements from a PLY file */ 150 | PlyPropRules *current_rules; /* current propagation rules */ 151 | PlyRuleList *rule_list; /* rule list from user */ 152 | } PlyFile; 153 | 154 | /* memory allocation */ 155 | /* 156 | extern char *my_alloc(); 157 | */ 158 | #define myalloc(mem_size) my_alloc((mem_size), __LINE__, __FILE__) 159 | 160 | 161 | /* old routines */ 162 | 163 | #if 0 164 | extern PlyFile *ply_write(FILE *, int, char **, int); 165 | extern PlyFile *ply_read(FILE *, int *, char ***); 166 | extern PlyFile *ply_open_for_reading( char *, int *, char ***, int *, float *); 167 | extern void ply_close(PlyFile *); 168 | extern PlyOtherProp *ply_get_other_properties(PlyFile *, char *, int); 169 | #endif 170 | 171 | extern void ply_describe_property(PlyFile *, char *, PlyProperty *); 172 | extern void ply_get_property(PlyFile *, char *, PlyProperty *); 173 | extern void ply_get_element(PlyFile *, void *); 174 | 175 | 176 | /*** delcaration of routines ***/ 177 | 178 | PlyOtherElems *get_other_element_ply (PlyFile *); 179 | 180 | PlyFile *read_ply(FILE *); 181 | PlyFile *write_ply(FILE *, int, char **, int); 182 | extern PlyFile *open_for_writing_ply(char *, int, char **, int); 183 | void close_ply(PlyFile *); 184 | void free_ply(PlyFile *); 185 | 186 | void get_info_ply(PlyFile *, float *, int *); 187 | void free_other_elements_ply (PlyOtherElems *); 188 | 189 | void append_comment_ply(PlyFile *, char *); 190 | void append_obj_info_ply(PlyFile *, char *); 191 | void copy_comments_ply(PlyFile *, PlyFile *); 192 | void copy_obj_info_ply(PlyFile *, PlyFile *); 193 | char **get_comments_ply(PlyFile *, int *); 194 | char **get_obj_info_ply(PlyFile *, int *); 195 | 196 | char **get_element_list_ply(PlyFile *, int *); 197 | void setup_property_ply(PlyFile *, PlyProperty *); 198 | void get_element_ply (PlyFile *, void *); 199 | char *setup_element_read_ply (PlyFile *, int, int *); 200 | PlyOtherProp *get_other_properties_ply(PlyFile *, int); 201 | 202 | void element_count_ply(PlyFile *, char *, int); 203 | void describe_element_ply(PlyFile *, char *, int); 204 | void describe_property_ply(PlyFile *, PlyProperty *); 205 | void describe_other_properties_ply(PlyFile *, PlyOtherProp *, int); 206 | void describe_other_elements_ply ( PlyFile *, PlyOtherElems *); 207 | void get_element_setup_ply(PlyFile *, char *, int, PlyProperty *); 208 | PlyProperty **get_element_description_ply(PlyFile *, char *, int*, int*); 209 | void element_layout_ply(PlyFile *, char *, int, int, PlyProperty *); 210 | 211 | void header_complete_ply(PlyFile *); 212 | void put_element_setup_ply(PlyFile *, char *); 213 | void put_element_ply(PlyFile *, void *); 214 | void put_other_elements_ply(PlyFile *); 215 | 216 | PlyPropRules *init_rule_ply (PlyFile *, char *); 217 | void modify_rule_ply (PlyPropRules *, char *, int); 218 | void start_props_ply (PlyFile *, PlyPropRules *); 219 | void weight_props_ply (PlyFile *, float, void *); 220 | void *get_new_props_ply(PlyFile *); 221 | void set_prop_rules_ply (PlyFile *, PlyRuleList *); 222 | PlyRuleList *append_prop_rule (PlyRuleList *, char *, char *); 223 | int matches_rule_name (char *); 224 | 225 | int equal_strings(char *, char *); 226 | char *recreate_command_line (int, char *argv[]); 227 | 228 | 229 | #ifdef __cplusplus 230 | } 231 | #endif 232 | #endif /* !__PLY_H__ */ 233 | 234 | -------------------------------------------------------------------------------- /tests/base_test.h: -------------------------------------------------------------------------------- 1 | /* This files stores common datastructures and functions required to run test using each of the benchmarks */ 2 | 3 | typedef struct options 4 | { 5 | bool verbose; 6 | char* input_filename; 7 | char* output_filename; 8 | } Opts; 9 | 10 | typedef struct vec3f 11 | { 12 | float x,y,z; 13 | } Vec3f; 14 | 15 | typedef struct tri 16 | { 17 | int32_t i1, i2, i3; 18 | } Tri; 19 | 20 | typedef struct triangle_mesh 21 | { 22 | int32_t n_verts; 23 | int32_t n_faces; 24 | Vec3f* vertices; 25 | Tri* faces; 26 | } TriMesh; 27 | 28 | 29 | bool read_ply( const char* filename, TriMesh* mesh, bool *is_binary ); 30 | void write_ply( const char* filename, TriMesh* mesh, bool is_binary ); 31 | 32 | int32_t 33 | parse_arguments( const char* program_name, int argc, char**argv, Opts* opts ) 34 | { 35 | msh_argparse_t parser; 36 | opts->input_filename = NULL; 37 | opts->output_filename = NULL; 38 | opts->verbose = 0; 39 | 40 | msh_ap_init( &parser, program_name, 41 | "This program simply reads and writes an input ply file" ); 42 | msh_ap_add_string_argument( &parser, "input_filename", NULL, "Name of a ply file to read", 43 | &opts->input_filename, 1 ); 44 | msh_ap_add_string_argument( &parser, "--output_filename", "-o", "Name of a ply file to write", 45 | &opts->output_filename, 1 ); 46 | msh_ap_add_bool_argument( &parser, "--verbose", "-v", "Print verbose information", 47 | &opts->verbose, 0 ); 48 | 49 | if( !msh_ap_parse(&parser, argc, argv) ) 50 | { 51 | return 1; 52 | } 53 | return 0; 54 | } 55 | 56 | int32_t 57 | run_test(const char* program_name, bool is_able_to_write_ply, int argc, char** argv ) 58 | { 59 | uint64_t t1, t2; 60 | Opts opts = {0}; 61 | TriMesh mesh = {0}; 62 | 63 | int parse_err = parse_arguments( program_name, argc, argv, &opts ); 64 | if( parse_err ) { return 1; } 65 | 66 | if (!is_able_to_write_ply) 67 | { 68 | opts.output_filename = NULL; 69 | } 70 | 71 | bool is_binary = false; 72 | msh_cprintf( opts.verbose, "Reading %s ...\n", opts.input_filename ); 73 | t1 = msh_time_now(); 74 | read_ply( opts.input_filename, &mesh, &is_binary ); 75 | t2 = msh_time_now(); 76 | double read_time = msh_time_diff_ms( t2, t1 ); 77 | 78 | double write_time = -1.0f; 79 | if( opts.output_filename ) 80 | { 81 | t1 = msh_time_now(); 82 | write_ply( opts.output_filename, &mesh, is_binary ); 83 | t2 = msh_time_now(); 84 | write_time = msh_time_diff_ms( t2, t1 ); 85 | } 86 | 87 | msh_cprintf( !opts.verbose, "%f %f\n", read_time, write_time ); 88 | 89 | msh_cprintf( opts.verbose, "Reading done in %lf ms\n", read_time ); 90 | msh_cprintf( opts.verbose && opts.output_filename, "Writing done in %lf ms\n", write_time ); 91 | msh_cprintf( opts.verbose, "N. Verts : %d; N. Faces: %d\n", mesh.n_verts, mesh.n_faces ); 92 | 93 | return 0; 94 | } -------------------------------------------------------------------------------- /tests/happly_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking of the read and write capabilities of happly by Nicolas Sharp (@nmwsharp) 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to storage. 7 | License: Public Domain 8 | 9 | Compilation: 10 | g++ -I -Ihapply/ -O2 -std=c++11 happly_test.cpp -o bin/happly_test 11 | 12 | */ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "happly/happly.h" 20 | 21 | #define MSH_STD_INCLUDE_LIBC_HEADERS 22 | #define MSH_STD_INCLUDE_HEADERS 23 | #define MSH_STD_IMPLEMENTATION 24 | #define MSH_ARGPARSE_IMPLEMENTATION 25 | #include "msh/msh_std.h" 26 | #include "msh/msh_argparse.h" 27 | #include "base_test.h" 28 | 29 | bool read_ply(const char *filename, TriMesh *mesh, bool *is_binary) 30 | { 31 | happly::PLYData plyIn(filename, false); 32 | std::vector x_pos = plyIn.getElement("vertex").getProperty("x"); 33 | std::vector y_pos = plyIn.getElement("vertex").getProperty("y"); 34 | std::vector z_pos = plyIn.getElement("vertex").getProperty("z"); 35 | std::vector> face_ind = plyIn.getElement("face").getListProperty("vertex_indices"); 36 | 37 | mesh->n_verts = (int)x_pos.size(); 38 | mesh->n_faces = (int)face_ind.size(); 39 | mesh->vertices = (Vec3f *)malloc(mesh->n_verts * sizeof(Vec3f)); 40 | mesh->faces = (Tri *)malloc(mesh->n_faces * sizeof(Tri)); 41 | for (int i = 0; i < mesh->n_verts; ++i) 42 | { 43 | Vec3f vertex = {x_pos[i], y_pos[i], z_pos[i]}; 44 | mesh->vertices[i] = vertex; 45 | } 46 | for (int i = 0; i < mesh->n_faces; ++i) 47 | { 48 | Tri triangle = {face_ind[i][0], face_ind[i][1], face_ind[i][2]}; 49 | mesh->faces[i] = triangle; 50 | } 51 | *is_binary = (plyIn.getInputDataFormat() != happly::DataFormat::ASCII); 52 | return true; 53 | } 54 | 55 | void write_ply(const char *filename, TriMesh *mesh, bool is_binary) 56 | { 57 | // Create an empty object 58 | happly::PLYData plyOut; 59 | 60 | plyOut.addElement("vertex", mesh->n_verts); 61 | plyOut.addElement("face", mesh->n_faces); 62 | 63 | std::vector xPos(mesh->n_verts); 64 | std::vector yPos(mesh->n_verts); 65 | std::vector zPos(mesh->n_verts); 66 | for (int i = 0; i < mesh->n_verts; i++) 67 | { 68 | xPos[i] = mesh->vertices[i].x; 69 | yPos[i] = mesh->vertices[i].y; 70 | zPos[i] = mesh->vertices[i].z; 71 | } 72 | 73 | plyOut.getElement("vertex").addProperty("x", xPos); 74 | plyOut.getElement("vertex").addProperty("y", yPos); 75 | plyOut.getElement("vertex").addProperty("z", zPos); 76 | 77 | std::vector> intInds; 78 | for (int i = 0; i < mesh->n_faces; ++i) 79 | { 80 | std::vector thisInds(3); 81 | thisInds[0] = mesh->faces[i].i1; 82 | thisInds[1] = mesh->faces[i].i2; 83 | thisInds[2] = mesh->faces[i].i3; 84 | intInds.push_back(thisInds); 85 | } 86 | 87 | // Store 88 | plyOut.getElement("face").addListProperty("vertex_indices", intInds); 89 | 90 | happly::DataFormat output_format = is_binary ? happly::DataFormat::Binary : happly::DataFormat::ASCII; 91 | 92 | plyOut.write(filename, output_format); 93 | } 94 | 95 | int main(int argc, char **argv) 96 | { 97 | bool is_able_to_write_ply = true; 98 | return run_test("happly_test", is_able_to_write_ply, argc, argv); 99 | } -------------------------------------------------------------------------------- /tests/microply_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 11/26/20 4 | Description: Bencharking of the read and write capabilities of micro_ply.h by Nick Klingensmith (@maluoi) 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to storage. 7 | License: Public Domain 8 | 9 | Compilation: 10 | GCC : g++ -I -Imshply/ -O2 -std=c11 mshply_test.c -o bin/mshply_test 11 | MSVC : 12 | 13 | Comments: micro_ply.h only supports ASCII .ply files. It also does not seem to support anyway of prespecifing list size. 14 | Additionally, it seems to silently crash if binary files are passed in. 15 | */ 16 | 17 | #define MSH_STD_INCLUDE_LIBC_HEADERS 18 | #define MSH_STD_IMPLEMENTATION 19 | #define MSH_ARGPARSE_IMPLEMENTATION 20 | #define MICRO_PLY_IMPL 21 | #include "msh/msh_std.h" 22 | #include "msh/msh_argparse.h" 23 | #include "micro_ply/micro_ply.h" 24 | #include "base_test.h" 25 | 26 | bool 27 | read_ply( const char* filename, TriMesh* mesh, bool *is_binary ) 28 | { 29 | // Read the data from the input file 30 | void *data; 31 | size_t size; 32 | FILE *fp; 33 | if ((fp = fopen(filename, "rb")) == nullptr) 34 | { 35 | return false; 36 | } 37 | fseek(fp, 0L, SEEK_END); 38 | size = ftell(fp); 39 | rewind(fp); 40 | data = malloc(size); 41 | fread (data, size, 1, fp); 42 | fclose(fp); 43 | 44 | // Parse the data using ply_read 45 | ply_file_t file; 46 | if (!ply_read(data, size, &file)) 47 | { 48 | return false; 49 | } 50 | 51 | float fzero = 0; 52 | ply_map_t map_verts[] = { 53 | { PLY_PROP_POSITION_X, ply_prop_decimal, sizeof(float), 0, &fzero }, 54 | { PLY_PROP_POSITION_Y, ply_prop_decimal, sizeof(float), 4, &fzero }, 55 | { PLY_PROP_POSITION_Z, ply_prop_decimal, sizeof(float), 8, &fzero } }; 56 | ply_convert(&file, PLY_ELEMENT_VERTICES, map_verts, msh_count_of(map_verts), sizeof(Vec3f), (void **)&mesh->vertices, &mesh->n_verts); 57 | 58 | uint32_t izero = 0; 59 | ply_map_t map_inds[] = { { PLY_PROP_INDICES, ply_prop_uint, sizeof(uint32_t), 0, &izero } }; 60 | ply_convert(&file, PLY_ELEMENT_FACES, map_inds, msh_count_of(map_inds), sizeof(uint32_t), (void **)&mesh->faces, &mesh->n_faces); 61 | mesh->n_faces /= 3; 62 | 63 | // You gotta free the memory manually! 64 | ply_free(&file); 65 | free(data); 66 | 67 | return true; 68 | } 69 | 70 | void 71 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 72 | { 73 | } 74 | 75 | int 76 | main( int argc, char** argv ) 77 | { 78 | bool is_able_to_write_ply = false; 79 | return run_test("microply_test", is_able_to_write_ply, argc, argv ); 80 | } 81 | -------------------------------------------------------------------------------- /tests/miniply_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 02/02/2020 4 | Description: Benchmarking the read and write capabilities of miniply by Vilya Harvey (@vilyah) 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | g++ -I -Iminiply/ -O2 -std=c++11 miniply/miniply.cpp miniply_test.cpp -o bin/miniply_test 11 | 12 | Notes: 13 | - miniply is super fast in general, but it especially excels in parsing ascii files. 14 | */ 15 | 16 | #define MSH_STD_INCLUDE_LIBC_HEADERS 17 | #define MSH_STD_INCLUDE_HEADERS 18 | #define MSH_STD_IMPLEMENTATION 19 | #define MSH_ARGPARSE_IMPLEMENTATION 20 | #include "msh/msh_std.h" 21 | #include "msh/msh_argparse.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "miniply/miniply.h" 28 | #include "base_test.h" 29 | 30 | // This is modified code from Vilya Harvey's ply-parsing-perf 31 | bool 32 | read_ply( const char* filename, TriMesh* mesh, bool *is_binary ) 33 | { 34 | int32_t verts_per_face = 3; 35 | 36 | miniply::PLYReader reader(filename); 37 | if (!reader.valid()) { 38 | return false; 39 | } 40 | 41 | std::vector listIdxs; 42 | miniply::PLYElement* facesElem = reader.get_element(reader.find_element(miniply::kPLYFaceElement)); 43 | if (facesElem != nullptr) 44 | { 45 | listIdxs.resize(verts_per_face); 46 | facesElem->convert_list_to_fixed_size( facesElem->find_property("vertex_indices"), verts_per_face, listIdxs.data()); 47 | } 48 | 49 | bool gotVerts = false; 50 | bool gotFaces = false; 51 | while (reader.has_element() && (!gotVerts || !gotFaces)) 52 | { 53 | if (!gotVerts && reader.element_is(miniply::kPLYVertexElement)) 54 | { 55 | if (!reader.load_element()) { break; } 56 | uint32_t propIdxs[3]; 57 | if (!reader.find_pos(propIdxs)) { break; } 58 | mesh->n_verts = reader.num_rows(); 59 | mesh->vertices = new Vec3f[mesh->n_verts]; 60 | reader.extract_properties(propIdxs, 3, miniply::PLYPropertyType::Float, mesh->vertices ); 61 | gotVerts = true; 62 | } 63 | else if (!gotFaces && reader.element_is(miniply::kPLYFaceElement)) 64 | { 65 | if (!reader.load_element()) { break; } 66 | mesh->n_faces = reader.num_rows(); 67 | mesh->faces = new Tri[mesh->n_faces]; 68 | reader.extract_properties(listIdxs.data(), verts_per_face, miniply::PLYPropertyType::Int, mesh->faces ); 69 | gotFaces = true; 70 | } 71 | reader.next_element(); 72 | } 73 | 74 | *is_binary = (reader.file_type() != miniply::PLYFileType::ASCII ); 75 | 76 | return true; 77 | } 78 | 79 | void 80 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 81 | { 82 | } 83 | 84 | int 85 | main( int argc, char** argv ) 86 | { 87 | bool is_able_to_write_ply = false; 88 | return run_test("miniply_test", is_able_to_write_ply, argc, argv ); 89 | } -------------------------------------------------------------------------------- /tests/mshply_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking the read and write capabilities of mshply 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | gcc -I -Imshply/ -O2 -std=c11 mshply_test.c -o bin/mshply_test 11 | 12 | */ 13 | 14 | #define MSH_STD_INCLUDE_LIBC_HEADERS 15 | #define MSH_STD_IMPLEMENTATION 16 | #define MSH_ARGPARSE_IMPLEMENTATION 17 | #define MSH_PLY_IMPLEMENTATION 18 | #include "msh/msh_std.h" 19 | #include "msh/msh_argparse.h" 20 | #include "msh/msh_ply.h" 21 | #include "base_test.h" 22 | 23 | const char* positions_names[] = { "x", "y", "z" }; 24 | const char* vertex_indices_names[] = { "vertex_indices" }; 25 | msh_ply_desc_t vertex_desc = { .element_name = "vertex", 26 | .property_names = positions_names, 27 | .num_properties = 3, 28 | .data_type = MSH_PLY_FLOAT}; 29 | msh_ply_desc_t face_desc = { .element_name = "face", 30 | .property_names = vertex_indices_names, 31 | .num_properties = 1, 32 | .data_type = MSH_PLY_INT32, 33 | .list_type = MSH_PLY_UINT8, 34 | .list_size_hint = 3 }; 35 | 36 | bool 37 | read_ply( const char* filename, TriMesh* mesh, bool *is_binary ) 38 | { 39 | vertex_desc.data = &mesh->vertices; 40 | vertex_desc.data_count = &mesh->n_verts; 41 | face_desc.data = &mesh->faces; 42 | face_desc.data_count = &mesh->n_faces; 43 | msh_ply_t* pf = msh_ply_open( filename, "rb"); 44 | if( pf ) 45 | { 46 | msh_ply_add_descriptor( pf, &vertex_desc ); 47 | msh_ply_add_descriptor( pf, &face_desc ); 48 | msh_ply_read( pf ); 49 | *is_binary = (pf->format != MSH_PLY_ASCII); 50 | msh_ply_close( pf ); 51 | return true; 52 | } 53 | else 54 | { 55 | msh_ply_close( pf ); 56 | return false; 57 | } 58 | } 59 | 60 | void 61 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 62 | { 63 | vertex_desc.data = &mesh->vertices; 64 | vertex_desc.data_count = &mesh->n_verts; 65 | face_desc.data = &mesh->faces; 66 | face_desc.data_count = &mesh->n_faces; 67 | const char* write_format = is_binary ? "wb" : "w"; 68 | msh_ply_t* pf = msh_ply_open( filename, write_format); 69 | if( pf ) 70 | { 71 | msh_ply_add_descriptor( pf, &vertex_desc ); 72 | msh_ply_add_descriptor( pf, &face_desc ); 73 | msh_ply_write(pf); 74 | } 75 | msh_ply_close(pf); 76 | } 77 | 78 | int 79 | main( int argc, char** argv ) 80 | { 81 | bool is_able_to_write_ply = true; 82 | return run_test("mshply_test", is_able_to_write_ply, argc, argv ); 83 | } 84 | -------------------------------------------------------------------------------- /tests/nanoply_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking the read and write capabilities of nanoply from vcglib @cnr-isti-vclab 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | g++ -I -Inanoply/ -O2 -std=c++11 nanoply_test.cpp -o bin/nanoply_test 11 | 12 | Notes: 13 | - There appears to be no way to do non-triangle meshes? 14 | - I had modify name property list variable for vertiex indices to say vertex_indices instead of vertex_index 15 | Line 249 of original nanoply. Not sure if I am using library wrong.. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "nanoply/nanoply.hpp" 25 | 26 | #define MSH_STD_INCLUDE_LIBC_HEADERS 27 | #define MSH_STD_INCLUDE_HEADERS 28 | #define MSH_STD_IMPLEMENTATION 29 | #define MSH_ARGPARSE_IMPLEMENTATION 30 | #include "msh/msh_std.h" 31 | #include "msh/msh_argparse.h" 32 | #include "base_test.h" 33 | 34 | bool 35 | read_ply( const char* filename, TriMesh* mesh, bool *is_binary ) 36 | { 37 | // Get file info 38 | nanoply::Info info( filename ); 39 | *is_binary = info.binary; 40 | 41 | // Prepare the mesh contents 42 | mesh->n_verts = (int32_t)info.GetVertexCount(); 43 | mesh->n_faces = (int32_t)info.GetFaceCount(); 44 | mesh->vertices = (Vec3f*)malloc( mesh->n_verts * sizeof(Vec3f) ); 45 | mesh->faces = (Tri*)malloc( mesh->n_faces * sizeof(Tri) ); 46 | 47 | // Create the vertex and face properties descriptor 48 | nanoply::ElementDescriptor vertex(nanoply::NNP_VERTEX_ELEM); 49 | nanoply::ElementDescriptor face(nanoply::NNP_FACE_ELEM); 50 | vertex.dataDescriptor.push_back(new nanoply::DataDescriptor( 51 | nanoply::NNP_PXYZ, (void*)mesh->vertices)); 52 | face.dataDescriptor.push_back(new nanoply::DataDescriptor( 53 | nanoply::NNP_FACE_VERTEX_LIST, (void*)mesh->faces)); 54 | 55 | // Create the mesh descriptor 56 | std::vector meshDescr; 57 | meshDescr.push_back(&vertex); 58 | meshDescr.push_back(&face); 59 | 60 | // Open the file and save the element data according the relative element descriptor 61 | OpenModel(info, meshDescr); 62 | 63 | // Cleanup 64 | for (int i = 0; i < vertex.dataDescriptor.size(); i++) 65 | delete vertex.dataDescriptor[i]; 66 | for (int i = 0; i < face.dataDescriptor.size(); i++) 67 | delete face.dataDescriptor[i]; 68 | return true; 69 | } 70 | 71 | void 72 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 73 | { 74 | //Create the vector of vertex properties to save in the file 75 | std::vector vertexProp; 76 | vertexProp.push_back(nanoply::PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_PXYZ)); 77 | 78 | //Create the vector of face properties to save in the file 79 | std::vector faceProp; 80 | faceProp.push_back(nanoply::PlyProperty(nanoply::NNP_LIST_UINT8_UINT32, nanoply::NNP_FACE_VERTEX_LIST)); 81 | 82 | //Create the PlyElement 83 | nanoply::PlyElement vertexElem(nanoply::NNP_VERTEX_ELEM, vertexProp, mesh->n_verts); 84 | nanoply::PlyElement faceElem(nanoply::NNP_FACE_ELEM, faceProp, mesh->n_faces); 85 | 86 | //Create the Info object with the data to save in the header 87 | nanoply::Info infoSave; 88 | infoSave.filename = filename; 89 | infoSave.binary = is_binary; 90 | infoSave.AddPlyElement(vertexElem); 91 | infoSave.AddPlyElement(faceElem); 92 | 93 | //Create the vertex properties descriptor (what ply property and where the data is stored) 94 | nanoply::ElementDescriptor vertex(nanoply::NNP_VERTEX_ELEM); 95 | if (mesh->n_verts > 0) 96 | { 97 | vertex.dataDescriptor.push_back(new nanoply::DataDescriptor(nanoply::NNP_PXYZ, (mesh->vertices))); 98 | } 99 | 100 | //Create the face properties descriptor (what ply property and where the data is stored) 101 | nanoply::ElementDescriptor face(nanoply::NNP_FACE_ELEM); 102 | if (mesh->n_faces > 0) 103 | { 104 | face.dataDescriptor.push_back(new nanoply::DataDescriptor(nanoply::NNP_FACE_VERTEX_LIST, (mesh->faces))); 105 | } 106 | //Create the mesh descriptor 107 | std::vector meshDescr; 108 | meshDescr.push_back(&vertex); 109 | meshDescr.push_back(&face); 110 | 111 | //Save the file 112 | bool result = nanoply::SaveModel(infoSave.filename, meshDescr, infoSave); 113 | 114 | for (int i = 0; i < vertex.dataDescriptor.size(); i++) 115 | delete vertex.dataDescriptor[i]; 116 | for (int i = 0; i < face.dataDescriptor.size(); i++) 117 | delete face.dataDescriptor[i]; 118 | } 119 | 120 | int 121 | main( int argc, char** argv ) 122 | { 123 | bool is_able_to_write_ply = true; 124 | return run_test("nanoly_test", is_able_to_write_ply, argc, argv ); 125 | } -------------------------------------------------------------------------------- /tests/plylib_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking the read and write capabilities of plylib by @cnr-isti-vclab 5 | I use ply file io code found in: https://github.com/cnr-isti-vclab/vcglib/tree/master/wrap/ply 6 | which I believe to be used in meshlab. Can someone confirm or deny? 7 | Example adapred from: https://github.com/cnr-isti-vclab/vcglib/blob/master/wrap/io_trimesh/import_ply.h 8 | Task is to get positions and vertex_indices from a ply file that describe 9 | triangular mesh and write that mesh back to hard drive. 10 | License: Public Domain 11 | 12 | Compilation: 13 | g++ -I -Iplylib/ -O2 -std=c++11 plylib/plylib.cpp plylib_test.cpp -o bin/plylib_test 14 | 15 | Notes: 16 | - Poor docs, hard to use 17 | - Requires additional datastructures that try to predict everything(?) 18 | - Decently flexible, other that the above point 19 | - Plylib does not have write support, writing is adapted as a separate function from: 20 | (https://github.com/cnr-isti-vclab/vcglib/blob/master/wrap/io_trimesh/export_ply.h) 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "plylib.h" 29 | 30 | #define MSH_STD_INCLUDE_LIBC_HEADERS 31 | #define MSH_STD_INCLUDE_HEADERS 32 | #define MSH_STD_IMPLEMENTATION 33 | #define MSH_ARGPARSE_IMPLEMENTATION 34 | #include "msh/msh_std.h" 35 | #include "msh/msh_argparse.h" 36 | 37 | typedef struct options 38 | { 39 | bool verbose; 40 | char* input_filename; 41 | char* output_filename; 42 | } Opts; 43 | 44 | typedef struct vec3f 45 | { 46 | float x,y,z; 47 | } Vec3f; 48 | 49 | typedef struct tri 50 | { 51 | int i1, i2, i3; 52 | } Tri; 53 | 54 | 55 | typedef struct triangle_mesh 56 | { 57 | int n_verts; 58 | int n_faces; 59 | Vec3f* vertices; 60 | Tri* faces; 61 | } TriMesh; 62 | 63 | struct LoadPly_VertAux 64 | { 65 | float p[3]; 66 | float n[3]; 67 | int flags; 68 | float q; // the confidence 69 | float intensity; 70 | unsigned char r; 71 | unsigned char g; 72 | unsigned char b; 73 | unsigned char a; 74 | unsigned char data[1]; 75 | float radius; 76 | float u,v,w; 77 | }; 78 | 79 | struct LoadPly_FaceAux 80 | { 81 | unsigned char size; 82 | int v[512]; 83 | int flags; 84 | float q; 85 | float texcoord[32]; 86 | unsigned char ntexcoord; 87 | int texcoordind; 88 | float colors[32]; 89 | unsigned char ncolors; 90 | 91 | unsigned char r; 92 | unsigned char g; 93 | unsigned char b; 94 | unsigned char a; 95 | 96 | unsigned char data[1]; 97 | }; 98 | 99 | void 100 | read_ply( const char* filename, TriMesh* mesh, bool * is_binary ) 101 | { 102 | using namespace vcg::ply; 103 | PlyFile pf; 104 | pf.Open(filename, PlyFile::MODE_READ); 105 | pf.AddToRead("vertex", "x", T_FLOAT, T_FLOAT,offsetof(LoadPly_VertAux,p),0,0,0,0,0 ); 106 | pf.AddToRead("vertex", "y", T_FLOAT, T_FLOAT,offsetof(LoadPly_VertAux,p)+sizeof(float),0,0,0,0,0 ); 107 | pf.AddToRead("vertex", "z", T_FLOAT, T_FLOAT,offsetof(LoadPly_VertAux,p)+2*sizeof(float),0,0,0,0,0 ); 108 | pf.AddToRead("face", "vertex_indices", T_INT, T_INT, offsetof(LoadPly_FaceAux,v), 1, 0, T_UCHAR, T_UCHAR, offsetof(LoadPly_FaceAux,size) ); 109 | LoadPly_VertAux va; 110 | LoadPly_FaceAux fa; 111 | *is_binary = ( pf.GetFormat() != F_ASCII ); 112 | for(int i=0;in_verts = n; 120 | mesh->vertices = (Vec3f*)malloc(sizeof(Vec3f)*n); 121 | for(j=0;jvertices[j].x = va.p[0]; 125 | mesh->vertices[j].y = va.p[1]; 126 | mesh->vertices[j].z = va.p[2]; 127 | } 128 | } 129 | else if( !strcmp( pf.ElemName(i),"face") ) 130 | { 131 | int j; 132 | pf.SetCurElement(i); 133 | mesh->n_faces = n; 134 | mesh->faces = (Tri*)malloc(sizeof(Tri)*n); 135 | for(j=0;jfaces[j].i1 = fa.v[0]; 139 | mesh->faces[j].i2 = fa.v[1]; 140 | mesh->faces[j].i3 = fa.v[2]; 141 | } 142 | } 143 | } 144 | pf.Destroy(); 145 | } 146 | 147 | // This is extremly simplified version of code in https://github.com/cnr-isti-vclab/vcglib/blob/master/wrap/io_trimesh/export_ply.h 148 | static int Save( const TriMesh* mesh, const char * filename, bool binary ) // V1.0 149 | { 150 | FILE * fpout; 151 | const char * hbin = "binary_little_endian"; 152 | const char * hasc = "ascii"; 153 | const char * h; 154 | const char* open_format; 155 | bool multit = false; 156 | 157 | if(binary) { h=hbin; open_format="wb"; } 158 | else { h=hasc; open_format="w"; } 159 | 160 | 161 | fpout = fopen(filename,open_format); 162 | if(fpout==NULL) { 163 | return 0; 164 | } 165 | fprintf(fpout, 166 | "ply\n" 167 | "format %s 1.0\n" 168 | "comment VCGLIB generated\n" 169 | ,h 170 | ); 171 | 172 | 173 | const char* vttp = "float"; 174 | fprintf(fpout,"element vertex %d\n",mesh->n_verts); 175 | fprintf(fpout,"property %s x\n", vttp); 176 | fprintf(fpout,"property %s y\n", vttp); 177 | fprintf(fpout,"property %s z\n", vttp); 178 | 179 | fprintf(fpout,"element face %d\n", mesh->n_faces ); 180 | fprintf(fpout,"property list uchar int vertex_indices\n" ); 181 | 182 | fprintf(fpout, "end_header\n" ); 183 | 184 | int j; 185 | for( j = 0; j < mesh->n_verts; j++) 186 | { 187 | if( binary ) 188 | { 189 | float t; 190 | 191 | t = mesh->vertices[j].x; fwrite(&t,sizeof(float),1,fpout); 192 | t = mesh->vertices[j].y; fwrite(&t,sizeof(float),1,fpout); 193 | t = mesh->vertices[j].z; fwrite(&t,sizeof(float),1,fpout); 194 | } 195 | else // ***** ASCII ***** 196 | { 197 | fprintf(fpout, "%g %g %g\n" ,mesh->vertices[j].x, mesh->vertices[j].y, mesh->vertices[j].z); 198 | } 199 | } 200 | 201 | /*vcg::tri::*/ 202 | 203 | char c = 3; 204 | int vv[3]; 205 | for( j = 0; j < mesh->n_faces; j++) 206 | { 207 | if(binary) 208 | { 209 | vv[0]=mesh->faces[j].i1; 210 | vv[1]=mesh->faces[j].i2; 211 | vv[2]=mesh->faces[j].i3; 212 | fwrite(&c,1,1,fpout); 213 | fwrite(vv,sizeof(int),3,fpout); 214 | } 215 | else // ***** ASCII ***** 216 | { 217 | fprintf(fpout,"%d " ,c); 218 | fprintf(fpout,"%d %d %d \n", mesh->faces[j].i1, mesh->faces[j].i2, mesh->faces[j].i3); 219 | } 220 | } 221 | fclose(fpout); 222 | return 0; 223 | } 224 | 225 | void 226 | write_ply( const char* filename, const TriMesh* mesh, bool is_binary ) 227 | { 228 | Save( mesh, filename, is_binary ); 229 | } 230 | 231 | int parse_arguments( int argc, char**argv, Opts* opts) 232 | { 233 | msh_argparse_t parser; 234 | opts->input_filename = NULL; 235 | opts->output_filename = (char*)"test.ply"; 236 | opts->verbose = 0; 237 | 238 | msh_ap_init( &parser, "mshply test", 239 | "This program simply reads and writes an input ply file" ); 240 | msh_ap_add_string_argument( &parser, "input_filename", NULL, "Name of a ply file to read", 241 | &opts->input_filename, 1 ); 242 | msh_ap_add_string_argument( &parser, "--output_filename", "-o", "Name of a ply file to write", 243 | &opts->output_filename, 1 ); 244 | msh_ap_add_bool_argument( &parser, "--verbose", "-v", "Print verbose information", 245 | &opts->verbose, 0 ); 246 | 247 | if( !msh_ap_parse(&parser, argc, argv) ) 248 | { 249 | return 1; 250 | } 251 | return 0; 252 | } 253 | 254 | int 255 | main( int argc, char** argv ) 256 | { 257 | uint64_t t1, t2; 258 | Opts opts = {0}; 259 | TriMesh mesh = {0}; 260 | 261 | int parse_err = parse_arguments( argc, argv, &opts ); 262 | if( parse_err ) { return 1; } 263 | 264 | bool is_binary = false; 265 | msh_cprintf( opts.verbose, "Reading %s ...\n", opts.input_filename ); 266 | t1 = msh_time_now(); 267 | read_ply( opts.input_filename, &mesh, &is_binary ); 268 | t2 = msh_time_now(); 269 | double read_time = msh_time_diff_ms( t2, t1 ); 270 | double write_time = -1.0f; 271 | if( opts.output_filename ) 272 | { 273 | t1 = msh_time_now(); 274 | write_ply( opts.output_filename, &mesh, is_binary ); 275 | t2 = msh_time_now(); 276 | write_time = msh_time_diff_ms( t2, t1 ); 277 | } 278 | msh_cprintf( !opts.verbose, "%f %f\n", read_time, write_time ); 279 | msh_cprintf( opts.verbose, "Reading done in %lf ms\n", read_time ); 280 | msh_cprintf( opts.verbose && opts.output_filename, "Writing done in %lf ms\n", write_time ); 281 | msh_cprintf( opts.verbose, "N. Verts : %d; N. Faces: %d\n", mesh.n_verts, mesh.n_faces ); 282 | 283 | return 0; 284 | } -------------------------------------------------------------------------------- /tests/rply_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking the read and write capabilities of rply by Diego Nehab 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | gcc -I -Irply/ -O2 -std=c11 rply/rply.c rply_test.c -o bin/rply_test 11 | 12 | Comments: 13 | - User needs to generate both requested types and approperiate callbacks to read the data. 14 | */ 15 | 16 | #define MSH_STD_INCLUDE_LIBC_HEADERS 17 | #define MSH_STD_INCLUDE_HEADERS 18 | #define MSH_STD_IMPLEMENTATION 19 | #define MSH_ARGPARSE_IMPLEMENTATION 20 | #include "msh/msh_std.h" 21 | #include "msh/msh_argparse.h" 22 | #include "rply.h" 23 | #include "rplyfile.h" 24 | #include "base_test.h" 25 | 26 | 27 | static int read_vertex_cb(p_ply_argument argument) { 28 | void *pdata; 29 | long idata; 30 | ply_get_argument_user_data(argument, &pdata, &idata); 31 | TriMesh* triangle_mesh = (TriMesh*)pdata; 32 | int val_idx = idata; 33 | double value = ply_get_argument_value(argument); 34 | 35 | switch(val_idx) 36 | { 37 | case 0: 38 | triangle_mesh->vertices[triangle_mesh->n_verts].x = (float)value; break; 39 | case 1: 40 | triangle_mesh->vertices[triangle_mesh->n_verts].y = (float)value; break; 41 | case 2: 42 | triangle_mesh->vertices[triangle_mesh->n_verts++].z = (float)value; break; 43 | } 44 | return 1; 45 | } 46 | 47 | static int read_face_cb(p_ply_argument argument) { 48 | void *pdata; 49 | ply_get_argument_user_data(argument, &pdata, NULL); 50 | TriMesh* triangle_mesh = (TriMesh*)pdata; 51 | double value = ply_get_argument_value(argument); 52 | 53 | long length, val_idx; 54 | ply_get_argument_property(argument, NULL, &length, &val_idx); 55 | switch (val_idx) { 56 | case 0: 57 | triangle_mesh->faces[triangle_mesh->n_faces].i1 = (int)value; break; 58 | case 1: 59 | triangle_mesh->faces[triangle_mesh->n_faces].i2 = (int)value; break; 60 | case 2: 61 | triangle_mesh->faces[triangle_mesh->n_faces++].i3 = (int)value; break; 62 | default: 63 | break; 64 | } 65 | return 1; 66 | } 67 | 68 | bool 69 | read_ply( const char* filename, TriMesh* mesh, bool* is_binary ) 70 | { 71 | p_ply ply = ply_open(filename, NULL, 0, NULL); 72 | if (!ply) return false; 73 | if (!ply_read_header(ply)) return false; 74 | p_ply_element element = NULL; 75 | while( (element = ply_get_next_element(ply, element)) ) 76 | { 77 | const char* name = NULL; 78 | long n_instances = -1; 79 | ply_get_element_info(element, &name, &n_instances); 80 | if(!strcmp(name, "vertex")) 81 | { 82 | mesh->n_verts = n_instances; 83 | mesh->vertices = (Vec3f*)malloc(mesh->n_verts*sizeof(Vec3f)); 84 | } 85 | if(!strcmp(name, "face")) 86 | { 87 | mesh->n_faces = n_instances; 88 | mesh->faces = (Tri*)malloc(mesh->n_faces*sizeof(Tri)); 89 | } 90 | } 91 | mesh->n_verts = 0; 92 | mesh->n_faces = 0; 93 | ply_set_read_cb(ply, "vertex", "x", read_vertex_cb, mesh, 0); 94 | ply_set_read_cb(ply, "vertex", "y", read_vertex_cb, mesh, 1); 95 | ply_set_read_cb(ply, "vertex", "z", read_vertex_cb, mesh, 2); 96 | ply_set_read_cb(ply, "face", "vertex_indices", read_face_cb, mesh, 0); 97 | if (!ply_read(ply)) return false; 98 | *is_binary = (ply_get_storage_mode(ply) != PLY_ASCII); 99 | ply_close(ply); 100 | return true; 101 | } 102 | 103 | //NOTE: This is based on example found in wjakob instant-meshes implementation 104 | void 105 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 106 | { 107 | e_ply_storage_mode mode = is_binary ? PLY_LITTLE_ENDIAN : PLY_ASCII; 108 | p_ply ply = ply_create(filename, mode, NULL, 0, NULL); 109 | if (!ply) return; 110 | ply_add_element( ply, "vertex", mesh->n_verts ); 111 | ply_add_scalar_property( ply, "x", PLY_FLOAT ); 112 | ply_add_scalar_property( ply, "y", PLY_FLOAT ); 113 | ply_add_scalar_property( ply, "z", PLY_FLOAT ); 114 | 115 | 116 | ply_add_element( ply, "face", mesh->n_faces ); 117 | ply_add_list_property( ply, "vertex_indices", PLY_UINT8, PLY_INT ); 118 | 119 | ply_write_header( ply ); 120 | 121 | for( int32_t i = 0; i < mesh->n_verts; ++i ) 122 | { 123 | ply_write( ply, mesh->vertices[i].x ); 124 | ply_write( ply, mesh->vertices[i].y ); 125 | ply_write( ply, mesh->vertices[i].z ); 126 | } 127 | 128 | for( int32_t i = 0; i < mesh->n_faces; ++i ) 129 | { 130 | ply_write( ply, 3 ); 131 | ply_write( ply, mesh->faces[i].i1 ); 132 | ply_write( ply, mesh->faces[i].i2 ); 133 | ply_write( ply, mesh->faces[i].i3 ); 134 | } 135 | 136 | ply_close(ply); 137 | } 138 | 139 | 140 | int 141 | main( int argc, char** argv ) 142 | { 143 | bool is_able_to_write_ply = true; 144 | return run_test("rply_test", is_able_to_write_ply, argc, argv ); 145 | } -------------------------------------------------------------------------------- /tests/tinyply21_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking the read and write capabilities of tinyply by @ddiakopoulos 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | g++ -I../dev -Itinyply2.1/ -O2 -std=c++11 tinyply2.1/tinyply.cpp tinyply21_test.cpp -o bin/tinyply21_test 11 | 12 | */ 13 | 14 | 15 | #define MSH_STD_INCLUDE_LIBC_HEADERS 16 | #define MSH_STD_IMPLEMENTATION 17 | #define MSH_ARGPARSE_IMPLEMENTATION 18 | #include "msh/msh_std.h" 19 | #include "msh/msh_argparse.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "tinyply.h" 29 | #include "base_test.h" 30 | 31 | 32 | bool 33 | read_ply( const char* filename, TriMesh* mesh, bool* is_binary ) 34 | { 35 | using namespace tinyply; 36 | std::ifstream ss(filename, std::ios::binary); 37 | PlyFile file; 38 | file.parse_header(ss); 39 | 40 | std::shared_ptr verts, faces; 41 | verts = file.request_properties_from_element("vertex", {"x", "y", "z"}); 42 | faces = file.request_properties_from_element("face", { "vertex_indices" }); 43 | 44 | file.read(ss); 45 | { 46 | *is_binary = file.is_binary_file(); 47 | mesh->n_verts = (int32_t)verts->count; 48 | mesh->n_faces = (int32_t)faces->count; 49 | mesh->vertices = (Vec3f*)malloc( verts->buffer.size_bytes() ); 50 | mesh->faces = (Tri*)malloc( faces->buffer.size_bytes() ); 51 | std::memcpy(mesh->vertices, verts->buffer.get(), verts->buffer.size_bytes() ); 52 | std::memcpy(mesh->faces, faces->buffer.get(), faces->buffer.size_bytes() ); 53 | } 54 | return true; 55 | } 56 | 57 | void 58 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 59 | { 60 | using namespace tinyply; 61 | std::filebuf fb; 62 | std::ios_base::openmode flags = std::ios::out; 63 | if( is_binary ) { flags |= std::ios::binary; } 64 | fb.open(filename, flags ); 65 | std::ostream outstream(&fb); 66 | PlyFile out_file; 67 | out_file.add_properties_to_element("vertex", { "x", "y", "z" }, 68 | Type::FLOAT32, mesh->n_verts, reinterpret_cast(mesh->vertices), Type::INVALID, 0); 69 | out_file.add_properties_to_element("face", { "vertex_indices" }, 70 | Type::UINT32, mesh->n_faces, reinterpret_cast((int*)&mesh->faces[0].i1), Type::UINT8, 3); 71 | out_file.write(outstream, is_binary); 72 | fb.close(); 73 | } 74 | 75 | 76 | int 77 | main( int argc, char** argv ) 78 | { 79 | bool is_able_to_write_ply = true; 80 | return run_test("tinyply21_test", is_able_to_write_ply, argc, argv ); 81 | } -------------------------------------------------------------------------------- /tests/tinyply22_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 08/12/18 4 | Description: Benchmarking the read and write capabilities of tinyply by @ddiakopoulos 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | g++ -I -Itinyply/ -O2 -std=c++11 tinyply22_test.cpp -o bin/tinyply22_test 11 | 12 | */ 13 | 14 | #define MSH_STD_INCLUDE_LIBC_HEADERS 15 | #define MSH_STD_INCLUDE_HEADERS 16 | #define MSH_STD_IMPLEMENTATION 17 | #define MSH_ARGPARSE_IMPLEMENTATION 18 | #define TINYPLY_IMPLEMENTATION 19 | #include "msh/msh_std.h" 20 | #include "msh/msh_argparse.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "tinyply22/tinyply.h" 30 | #include "base_test.h" 31 | 32 | bool 33 | read_ply( const char* filename, TriMesh* mesh, bool *is_binary) 34 | { 35 | using namespace tinyply; 36 | std::ifstream ss(filename, std::ios::binary); 37 | PlyFile file; 38 | file.parse_header(ss); 39 | 40 | std::shared_ptr verts, faces; 41 | verts = file.request_properties_from_element("vertex", {"x", "y", "z"}); 42 | faces = file.request_properties_from_element("face", { "vertex_indices" }, 3); 43 | 44 | file.read(ss); 45 | { 46 | *is_binary = file.is_binary_file(); 47 | mesh->n_verts = (int32_t)verts->count; 48 | mesh->n_faces = (int32_t)faces->count; 49 | mesh->vertices = (Vec3f*)malloc( verts->buffer.size_bytes() ); 50 | mesh->faces = (Tri*)malloc( faces->buffer.size_bytes() ); 51 | std::memcpy(mesh->vertices, verts->buffer.get(), verts->buffer.size_bytes() ); 52 | std::memcpy(mesh->faces, faces->buffer.get(), faces->buffer.size_bytes() ); 53 | } 54 | return true; 55 | } 56 | 57 | void 58 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 59 | { 60 | using namespace tinyply; 61 | std::filebuf fb; 62 | std::ios_base::openmode flags = std::ios::out; 63 | if( is_binary ) { flags |= std::ios::binary; } 64 | fb.open(filename, flags); 65 | std::ostream outstream(&fb); 66 | PlyFile out_file; 67 | out_file.add_properties_to_element("vertex", { "x", "y", "z" }, 68 | Type::FLOAT32, mesh->n_verts, reinterpret_cast(mesh->vertices), Type::INVALID, 0); 69 | out_file.add_properties_to_element("face", { "vertex_indices" }, 70 | Type::UINT32, mesh->n_faces, reinterpret_cast((int*)&mesh->faces[0].i1), Type::UINT8, 3); 71 | out_file.write(outstream, is_binary); 72 | fb.close(); 73 | } 74 | 75 | int 76 | main( int argc, char** argv ) 77 | { 78 | bool is_able_to_write_ply = true; 79 | return run_test("tinyply22_test", is_able_to_write_ply, argc, argv ); 80 | } -------------------------------------------------------------------------------- /tests/tinyply23_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 08/12/18 4 | Description: Benchmarking the read and write capabilities of tinyply by @ddiakopoulos 5 | Task is to get positions and vertex_indices from a ply file that describe 6 | triangular mesh and write that mesh back to hard drive. 7 | License: Public Domain 8 | 9 | Compilation: 10 | g++ -I -Itinyply/ -O2 -std=c++11 tinyply2_test.cpp -o bin/tinyply2_test 11 | 12 | */ 13 | 14 | #define MSH_STD_INCLUDE_LIBC_HEADERS 15 | #define MSH_STD_INCLUDE_HEADERS 16 | #define MSH_STD_IMPLEMENTATION 17 | #define MSH_ARGPARSE_IMPLEMENTATION 18 | #define TINYPLY_IMPLEMENTATION 19 | #include "msh/msh_std.h" 20 | #include "msh/msh_argparse.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "tinyply23/tinyply.h" 30 | #include "base_test.h" 31 | 32 | bool 33 | read_ply( const char* filename, TriMesh* mesh, bool* is_binary ) 34 | { 35 | using namespace tinyply; 36 | std::ifstream ss(filename, std::ios::binary); 37 | PlyFile file; 38 | file.parse_header(ss); 39 | 40 | std::shared_ptr verts, faces; 41 | verts = file.request_properties_from_element("vertex", {"x", "y", "z"}); 42 | faces = file.request_properties_from_element("face", { "vertex_indices" }, 3); 43 | 44 | file.read(ss); 45 | { 46 | *is_binary = file.is_binary_file(); 47 | mesh->n_verts = (int32_t)verts->count; 48 | mesh->n_faces = (int32_t)faces->count; 49 | mesh->vertices = (Vec3f*)malloc( verts->buffer.size_bytes() ); 50 | mesh->faces = (Tri*)malloc( faces->buffer.size_bytes() ); 51 | std::memcpy(mesh->vertices, verts->buffer.get(), verts->buffer.size_bytes() ); 52 | std::memcpy(mesh->faces, faces->buffer.get(), faces->buffer.size_bytes() ); 53 | } 54 | return true; 55 | } 56 | 57 | void 58 | write_ply( const char* filename, TriMesh* mesh, bool is_binary ) 59 | { 60 | using namespace tinyply; 61 | std::filebuf fb; 62 | std::ios_base::openmode flags = std::ios::out; 63 | if( is_binary ) { flags |= std::ios::binary; } 64 | fb.open(filename, flags ); 65 | std::ostream outstream(&fb); 66 | PlyFile out_file; 67 | out_file.add_properties_to_element("vertex", { "x", "y", "z" }, 68 | Type::FLOAT32, mesh->n_verts, reinterpret_cast(mesh->vertices), Type::INVALID, 0); 69 | out_file.add_properties_to_element("face", { "vertex_indices" }, 70 | Type::UINT32, mesh->n_faces, reinterpret_cast((int*)&mesh->faces[0].i1), Type::UINT8, 3); 71 | out_file.write(outstream, is_binary); 72 | fb.close(); 73 | } 74 | 75 | int 76 | main( int argc, char** argv ) 77 | { 78 | bool is_able_to_write_ply = true; 79 | return run_test("tinyply_test23", is_able_to_write_ply, argc, argv ); 80 | } -------------------------------------------------------------------------------- /tests/turkply_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Maciej Halber 3 | Date: 04/09/18 4 | Description: Benchmarking the read and write capabilities of turkply, which is the original code for ply file parsing by Greg Turk, with name changed to differentiate it from other lubraries.. 5 | Setting is simple - getting positions and vertex_indices from a ply file that describes 6 | triangular mesh. 7 | License: Public Domain 8 | 9 | Compilation: 10 | gcc -I -Iturkply/ -O2 -std=c11 turkply/ply_io.c turkply_test.c -o bin/turkply_test 11 | 12 | Notes: 13 | - turkply does not seem deal with the endianness correctly 14 | - turkply used drand48() which is posix function, not available on Windows. 15 | Replaced it with (float)rand()/(float)(RAND_MAX) 16 | - turkply forces a specific representation of the mesh's face, hence I cannot reuse base_test.h 17 | - turkply generates a lot of warnings on MSVC, I suppresed them, but the library should be ideally fixed. 18 | */ 19 | 20 | #define MSH_STD_INCLUDE_LIBC_HEADERS 21 | #define MSH_STD_IMPLEMENTATION 22 | #define MSH_ARGPARSE_IMPLEMENTATION 23 | #include "msh/msh_std.h" 24 | #include "msh/msh_argparse.h" 25 | 26 | #include "ply_io.h" 27 | 28 | typedef struct options 29 | { 30 | bool verbose; 31 | char* input_filename; 32 | char* output_filename; 33 | } Opts; 34 | 35 | typedef struct vec3f 36 | { 37 | float x,y,z; 38 | } Vec3f; 39 | 40 | typedef struct face 41 | { 42 | unsigned char count; 43 | int* vertex_indices; 44 | } Face; 45 | 46 | typedef struct triangle_mesh 47 | { 48 | int n_verts; 49 | int n_faces; 50 | Vec3f* vertices; 51 | Face* faces; 52 | } TriMesh; 53 | 54 | char *elem_names[] = { 55 | "vertex", "face" 56 | }; 57 | 58 | PlyProperty vert_props[] = { 59 | {"x", Float32, Float32, offsetof(Vec3f,x), 0, 0, 0, 0}, 60 | {"y", Float32, Float32, offsetof(Vec3f,y), 0, 0, 0, 0}, 61 | {"z", Float32, Float32, offsetof(Vec3f,z), 0, 0, 0, 0}, 62 | }; 63 | 64 | 65 | PlyProperty face_props[] = { 66 | {"vertex_indices", Int32, Int32, offsetof(Face,vertex_indices), 1, Uint8, Uint8, offsetof(Face,count)}, 67 | }; 68 | 69 | PlyFile *in_ply; 70 | PlyFile *out_ply; 71 | 72 | void 73 | read_ply_file( const char* filename, TriMesh* mesh, bool *is_binary ) 74 | { 75 | int elem_count; 76 | char *elem_name; 77 | int i; 78 | int j; 79 | 80 | FILE *fp = fopen(filename, "rb"); 81 | if (!fp) { return; } 82 | in_ply = read_ply (fp ); 83 | 84 | for( i = 0; i < in_ply->num_elem_types; i++) 85 | { 86 | elem_name = setup_element_read_ply(in_ply, i, &elem_count); 87 | 88 | if( !strcmp("vertex", elem_name) ) 89 | { 90 | mesh->vertices = (Vec3f*)malloc( sizeof(Vec3f) * elem_count); 91 | mesh->n_verts = elem_count; 92 | 93 | setup_property_ply( in_ply, &vert_props[0] ); 94 | setup_property_ply( in_ply, &vert_props[1] ); 95 | setup_property_ply( in_ply, &vert_props[2] ); 96 | 97 | for (j = 0; j < elem_count; j++) { 98 | get_element_ply( in_ply, (void *)&mesh->vertices[j] ); 99 | } 100 | } 101 | 102 | if (!strcmp("face", elem_name)) 103 | { 104 | mesh->n_faces = elem_count; 105 | mesh->faces = (Face*)malloc( sizeof(Face) * elem_count ); 106 | 107 | setup_property_ply( in_ply, &face_props[0] ); 108 | 109 | for (j = 0; j < elem_count; j++) { 110 | get_element_ply( in_ply, (void *)&mesh->faces[j] ); 111 | } 112 | } 113 | } 114 | *is_binary = (in_ply->file_type != PLY_ASCII); 115 | close_ply (in_ply); 116 | } 117 | 118 | void 119 | write_ply_file( char* filename, const TriMesh* mesh, bool is_binary ) 120 | { 121 | int i; 122 | int num_elem_types; 123 | FILE* output = NULL; 124 | output = fopen( filename, "wb" ); 125 | if( output ==NULL ) return; 126 | int file_type = is_binary ? PLY_BINARY_LE: PLY_ASCII; 127 | 128 | out_ply = write_ply( output, 2, elem_names, file_type ); 129 | /* 130 | Describe what properties go into the vertex elements. 131 | */ 132 | describe_element_ply( out_ply, "vertex", mesh->n_verts ); 133 | describe_property_ply( out_ply, &vert_props[0] ); 134 | describe_property_ply( out_ply, &vert_props[1] ); 135 | describe_property_ply( out_ply, &vert_props[2] ); 136 | 137 | /* 138 | Describe what properties go into the face elements. 139 | */ 140 | describe_element_ply( out_ply, "face", mesh->n_faces ); 141 | describe_property_ply( out_ply, &face_props[0] ); 142 | 143 | header_complete_ply( out_ply ); 144 | /* 145 | Set up and write the vertex elements. 146 | */ 147 | put_element_setup_ply( out_ply, "vertex" ); 148 | for (i = 0; i < mesh->n_verts; i++) 149 | { 150 | put_element_ply( out_ply, (void *) &mesh->vertices[i] ); 151 | } 152 | /* 153 | Set up and write the face elements. 154 | */ 155 | put_element_setup_ply( out_ply, "face" ); 156 | for (i = 0; i < mesh->n_faces; i++) 157 | { 158 | put_element_ply( out_ply, (void *) &mesh->faces[i] ); 159 | } 160 | 161 | close_ply( out_ply ); 162 | fclose(output); 163 | } 164 | 165 | 166 | int 167 | parse_arguments( int argc, char**argv, Opts* opts) 168 | { 169 | msh_argparse_t parser; 170 | opts->input_filename = NULL; 171 | opts->output_filename = NULL; 172 | opts->verbose = 0; 173 | 174 | msh_ap_init( &parser, "turkply test", 175 | "This program simply reads and writes an input ply file" ); 176 | msh_ap_add_string_argument( &parser, "input_filename", NULL, "Name of a ply file to read", 177 | &opts->input_filename, 1 ); 178 | msh_ap_add_string_argument( &parser, "--output_filename", "-o", "Name of a ply file to write", 179 | &opts->output_filename, 1 ); 180 | msh_ap_add_bool_argument( &parser, "--verbose", "-v", "Print verbose information", 181 | &opts->verbose, 0 ); 182 | 183 | if( !msh_ap_parse(&parser, argc, argv) ) 184 | { 185 | msh_ap_display_help( &parser ); 186 | return 1; 187 | } 188 | return 0; 189 | } 190 | 191 | int 192 | main( int argc, char** argv ) 193 | { 194 | uint64_t t1, t2; 195 | Opts opts = {0}; 196 | TriMesh mesh = {0}; 197 | 198 | int parse_err = parse_arguments( argc, argv, &opts ); 199 | if( parse_err ) { return 1; } 200 | 201 | bool is_binary = false; 202 | msh_cprintf( opts.verbose, "Reading %s ...\n", opts.input_filename ); 203 | t1 = msh_time_now(); 204 | read_ply_file( opts.input_filename, &mesh, &is_binary ); 205 | t2 = msh_time_now(); 206 | double read_time = msh_time_diff_ms( t2, t1 ); 207 | 208 | double write_time = -1.0f; 209 | if( opts.output_filename ) 210 | { 211 | t1 = msh_time_now(); 212 | write_ply_file( opts.output_filename, &mesh, is_binary ); 213 | t2 = msh_time_now(); 214 | write_time = msh_time_diff_ms( t2, t1 ); 215 | } 216 | 217 | msh_cprintf( !opts.verbose, "%f %f\n", read_time, write_time ); 218 | 219 | msh_cprintf( opts.verbose, "Reading done in %lf ms\n", read_time ); 220 | msh_cprintf( opts.verbose && opts.output_filename, "Writing done in %lf ms\n", write_time ); 221 | msh_cprintf( opts.verbose, "N. Verts : %d; N. Faces: %d\n", mesh.n_verts, mesh.n_faces ); 222 | 223 | free_ply( out_ply ); 224 | free_ply( in_ply ); 225 | 226 | return 0; 227 | } --------------------------------------------------------------------------------