All Projects → vilya → miniply

vilya / miniply

Licence: MIT license
A fast and easy-to-use PLY parsing library in a single c++11 header and cpp file

Programming Languages

C++
36643 projects - #6 most used programming language
CMake
9771 projects

Projects that are alternatives of or similar to miniply

Tinyply
🌍 C++11 ply 3d mesh format importer & exporter
Stars: ✭ 358 (+1134.48%)
Mutual labels:  geometry, file-format
go-obj
OBJ file loader for golang
Stars: ✭ 16 (-44.83%)
Mutual labels:  file-format
geofeatures2
A lightweight, high performance geometry library in Swift.
Stars: ✭ 18 (-37.93%)
Mutual labels:  geometry
UnityPlyLoader
Load ply mesh file in Unity use vcglib
Stars: ✭ 23 (-20.69%)
Mutual labels:  ply
polliwog
2D and 3D computational geometry library
Stars: ✭ 22 (-24.14%)
Mutual labels:  geometry
laravel-geometry
SPINEN's Laravel wrapper over geoPHP
Stars: ✭ 36 (+24.14%)
Mutual labels:  geometry
vg
Vector-geometry toolbelt for 3D points and vectors
Stars: ✭ 106 (+265.52%)
Mutual labels:  geometry
pcc geo cnn v2
Improved Deep Point Cloud Geometry Compression
Stars: ✭ 55 (+89.66%)
Mutual labels:  geometry
s2
R bindings for Google's s2 library for geometry on the sphere
Stars: ✭ 29 (+0%)
Mutual labels:  geometry
cas
Cellular Automata Simulator
Stars: ✭ 22 (-24.14%)
Mutual labels:  geometry
tinymesh
TinyMesh is a light-weight mesh processing library in C/C++.
Stars: ✭ 64 (+120.69%)
Mutual labels:  geometry
liang-barsky
Liang-Barsky line-clipping algorithm
Stars: ✭ 32 (+10.34%)
Mutual labels:  geometry
mmtf
The specification of the MMTF format for biological structures
Stars: ✭ 40 (+37.93%)
Mutual labels:  file-format
ncdfgeom
NetCDF-CF Geometry and Timeseries Tools for R: https://code.usgs.gov/water/ncdfgeom
Stars: ✭ 13 (-55.17%)
Mutual labels:  geometry
polygon-splitter
A small (<10kb minified) javascript library for splitting polygons by a polyline.
Stars: ✭ 20 (-31.03%)
Mutual labels:  geometry
mikktspace-wasm
MikkTSpace vertex tangent calculation for JavaScript/TypeScript/Node.js, using Web Assembly.
Stars: ✭ 19 (-34.48%)
Mutual labels:  geometry
euclid
Vector math, geometry, reference frame, and shapes 2D & 3D
Stars: ✭ 22 (-24.14%)
Mutual labels:  geometry
geometry
Geometric primitives for Ruby
Stars: ✭ 46 (+58.62%)
Mutual labels:  geometry
spatial-algorithms
Spatial algorithms library for geometry.hpp
Stars: ✭ 28 (-3.45%)
Mutual labels:  geometry
MagicaVoxel File Writer
MagicaVoxel File Writer dependency free cpp class
Stars: ✭ 26 (-10.34%)
Mutual labels:  file-format

miniply

A fast and easy-to-use library for parsing PLY files, in a single c++11 header and cpp file with no external dependencies, ready to drop into your project.

Features

  • Fast: loads all 8929 PLY files from the pbrt-v3-scenes repository in under 9 seconds total - an average parsing time of less than 1 millisecond per file!
  • Small: just a single .h and .cpp file which you can copy into your project.
  • Complete: parses ASCII, binary little-endian and binary big-endian versions of the file format (binary loading assumes you're running on a little-endian CPU).
  • Cross-platform: works on Windows, macOS and Linux.
  • Provides helper methods for getting standard vertex and face properties.
  • Can triangulate polygons as they're loaded.
  • Fast path for models where you know every face has the same fixed number of vertices
  • MIT license

Note that miniply does not support writing PLY files, only reading them.

Getting started

  • Copy miniply.h and miniply.cpp into your project.
  • Add #include <miniply.h> wherever necessary.

The CMake file that you see in this repo is purely for building the miniply-info and miniply-perf command line tools in the extra folder; it isn't required if you're just using the library in your own project.

General use

The general usage model for this library is:

  1. Construct a PLYReader object. This will open the PLY file and read the header.
  2. Call reader.valid() to check that the file was opened successfully.
  3. Iterate over the elements using something like for (; reader.has_element(); reader.next_element()) { ... }, calling reader.element_is() to check whether the current element is one that you want data from.
  4. For any elements that you're interested in:
    • Call reader.load_element().
    • Use some combination of the extract_columns(), extract_list_column() and extract_triangles() methods to get the data you're interested in.

You can only iterate forwards over the elements in the file (at present). You cannot jump back to an earlier element.

You can skip forward to the next element simply by calling next_element() without having called load_element() yet. This will be very efficient if the current element is fixed-size. If the current element contains any list properties then we will have to scan through all of the element data to find where it finishes and the next element starts, which is not as efficient (although the library will do it's best!).

Loading header info from a PLY file

This function (taken directly from extra/miniply-info.cpp) shows how you can get the header from a .ply file:

bool print_ply_header(const char* filename)
{
  miniply::PLYReader reader(filename);
  if (!reader.valid()) {
    fprintf(stderr, "Failed to open %s\n", filename);
    return false;
  }

  printf("ply\n");
  printf("format %s %d.%d\n", kFileTypes[int(reader.file_type())],
         reader.version_major(), reader.version_minor());
  for (uint32_t i = 0, endI = reader.num_elements(); i < endI; i++) {
    const miniply::PLYElement* elem = reader.get_element(i);
    printf("element %s %u\n", elem->name.c_str(), elem->count);
    for (const miniply::PLYProperty& prop : elem->properties) {
      if (prop.countType != miniply::PLYPropertyType::None) {
        printf("property list %s %s %s\n", kPropertyTypes[uint32_t(prop.countType)],
               kPropertyTypes[uint32_t(prop.type)], prop.name.c_str());
      }
      else {
        printf("property %s %s\n", kPropertyTypes[uint32_t(prop.type)], prop.name.c_str());
      }
    }
  }
  printf("end_header\n");

  return true;
}

Loading a triangle mesh

Polygons in PLY files are ordinarily stored in a list property, meaning that each polygon is a variable-length list of vertex indices. If the mesh representation in your program is triangles-only, you will need to triangulate the faces. miniply has built-in support for this:

Note that if you know in advance that your PLY file only contains triangles, there is a much faster way to load it. See below for details.

// Very basic triangle mesh struct, for example purposes
struct TriMesh {
  // Per-vertex data
  float* pos     = nullptr; // has 3 * numVerts elements.
  float* uv      = nullptr; // if non-null, has 2 * numVerts elements.
  uint32_t numVerts   = 0;

  // Per-index data
  int* indices   = nullptr;
  uint32_t numIndices = 0; // number of indices = 3 times the number of triangles.
};


TriMesh* load_trimesh_from_ply(const char* filename)
{
  miniply::PLYReader reader(filename);
  if (!reader.valid()) {
    return nullptr;
  }

  uint32_t indexes[3];
  bool gotVerts = false, gotFaces = false;

  TriMesh* trimesh = new TriMesh();
  while (reader.has_element() && (!gotVerts || !gotFaces)) {
    if (reader.element_is(miniply::kPLYVertexElement) && reader.load_element() && reader.find_pos(indexes)) {
      trimesh->numVerts = reader.num_rows();
      trimesh->pos = new float[trimesh->numVerts * 3];
      reader.extract_properties(indexes, 3, miniply::PLYPropertyType::Float, trimesh->pos);
      if (reader.find_texcoord(indexes)) {
        trimesh->uv = new float[trimesh->numVerts * 2];
        reader.extract_properties(indexes, 2, miniply::PLYPropertyType::Float, trimesh->uv);
      }
      gotVerts = true;
    }
    else if (reader.element_is(miniply::kPLYFaceElement) && reader.load_element() && reader.find_indices(indexes)) {
      bool polys = reader.requires_triangulation(indexes[0]);
      if (polys && !gotVerts) {
        fprintf(stderr, "Error: need vertex positions to triangulate faces.\n");
        break;
      }
      if (polys) {
        trimesh->numIndices = reader.num_triangles(indexes[0]) * 3;
        trimesh->indices = new int[trimesh->numIndices];
        reader.extract_triangles(indexes[0], trimesh->pos, trimesh->numVerts, miniply::PLYPropertyType::Int, trimesh->indices);
      }
      else {
        trimesh->numIndices = reader.num_rows() * 3;
        trimesh->indices = new int[trimesh->numIndices];
        reader.extract_list_property(indexes[0], miniply::PLYPropertyType::Int, trimesh->indices);
      }
      gotFaces = true;
    }
    if (gotVerts && gotFaces) {
      break;
    }
    reader.next_element();
  }

  if (!gotVerts || !gotFaces) {
    delete trimesh;
    return nullptr;
  }

  return trimesh;
}

For a more complete example, see extra/miniply-perf.cpp

Loading from a PLY file known to only contain triangles

Loading the vertex indices for each face from a variable length list is a bit wasteful if you know ahread of time that your PLY file only contains triangles. With miniply you can take advantage of this knowledge to get a massive reduction in the loading time for the file.

The idea is to replace the single list property, which miniply has to treat as variable-sized, with a set of fixed-size properties. There will be one property corresponding to the item count for each list (which we will ignore during loading, because we know it will always be three), followed by three new properties (one for each list index). You do this by calling convert_list_to_fixed_size() on the face element at some point prior to loading its data.

Doing this allows miniply to use its far more efficient code path for loading fixed-size elements instead. This can cut loading times by more than half!

// Note: using the same TriMesh class as the example above, omitting it here
// for the sake of brevity.

TriMesh* load_trimesh_from_triangles_only_ply(const char* filename)
{
  miniply::PLYReader reader(filename);
  if (!reader.valid()) {
    return nullptr;
  }

  uint32_t faceIdxs[3];
  miniply::PLYElement* faceElem = reader.get_element(reader.find_element(miniply::kPLYFaceElement));
  if (faceElem == nullptr) {
    return nullptr;
  }
  faceElem->convert_list_to_fixed_size(faceElem->find_property("vertex_indices"), 3, faceIdxs);

  uint32_t indexes[3];
  bool gotVerts = false, gotFaces = false;

  TriMesh* trimesh = new TriMesh();
  while (reader.has_element() && (!gotVerts || !gotFaces)) {
    if (reader.element_is(miniply::kPLYVertexElement) && reader.load_element() && reader.find_pos(indexes)) {
      // This section is the same as the example above, not repeating it here.
    }
    else if (!gotFaces && reader.element_is(miniply::kPLYFaceElement) && reader.load_element()) {
      trimesh->numIndices = reader.num_rows() * 3;
      trimesh->indices = new int[trimesh->numIndices];
      reader.extract_properties(faceIdxs, 3, miniply::PLYPropertyType::Int, trimesh->indices);
      gotFaces = true;
    }
    if (gotVerts && gotFaces) {
      break;
    }
    reader.next_element();
  }

  if (!gotVerts || !gotFaces) {
    delete trimesh;
    return nullptr;
  }

  return trimesh;
}

To recap, the differences from the previous example are:

  1. We're calling faceElem->convert_list_to_fixed_size() up front.
  2. In the section which processes the face element, we're calling reader.extract_properties() to get the index data instead of reader.extract_triangles() or reader.extrat_list_property().

History

I originally wrote the code that became miniply as part of minipbrt. I looked at several existing PLY parsing libraries first, but (a) I really wanted to keep minipbrt dependency free; and (b) I already had a lot of parsing code lying around just begging to be reused. In the end, the code I wrote for minipbrt seemed complete enough to be worth publishing as a standalone library too. Since then I've refined the API, improved performance and reduced memory usage. I hope you like the result. :-)

Performance

The ply-parsing-perf repo has a detailed performance comparison between miniply and a number of other ply parsing libraries.

Overall miniply is between 2 and 8 times faster than all the other parsers I've tested, for that workload (creating a simple poly mesh from each ply file).

See also Maciej Halber's ply_io_benchmark (thanks to Dimitri Diakopoulos for the pointer!) for more performance comparisons.

Other PLY parsing libraries

If miniply doesn't meet your needs, perhaps one of these other great PLY parsing libraries will?

In particular these all support writing as well as reading, whereas miniply only supports reading.

Feedback, suggestions and bug reports

GitHub issues: https://github.com/vilya/miniply/issues

If you're using miniply and find it useful, drop me an email - I'd love to know about it!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].