Skip to content

Instantly share code, notes, and snippets.

@akingdom
Last active September 29, 2023 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akingdom/b56a60871a788a7c1661bdf3cd8c320b to your computer and use it in GitHub Desktop.
Save akingdom/b56a60871a788a7c1661bdf3cd8c320b to your computer and use it in GitHub Desktop.

The PLY Polygon File Format

  1. Main Article (Author: Greg Turk)

  2. Clarifications (Author: Andrew Kingdom)


Main Article

Author: Greg Turk

Introduction

This document presents the PLY polygon file format, a format for storing graphical objects that are described as a collection of polygons. Our goal is to provide a format that is simple and easy to implement but that is general enough to be useful for a wide range of models. The file format has two sub-formats: an ASCII representation for easily getting started, and a binary version for compact storage and for rapid saving and loading. We hope that this format will promote the exchange of graphical object between programs and also between groups of people.

Overview

Anyone who has worked in the field of computer graphics for even a short time knows about the bewildering array of storage formats for graphical objects. It seems as though every programmer creates a new file format for nearly every new programming project. The way out of this morass of formats is to create a single file format that is both flexible enough to anticipate future needs and that is simple enough so as not to drive away potential users. Once such a format is defined, a suite of utilities (both procedures and entire programs) can be written that are centered around this format. Each new utility that is added to the suite can leverage off the power of the others.

The PLY format describes an object as a collection of vertices, faces and other elements, along with properties such as color and normal direction that can be attached to these elements. A PLY file contains the description of exactly one object. Sources of such objects include: hand-digitized objects, polygon objects from modeling programs, range data, triangles from marching cubes (isosurfaces from volume data), terrain data, radiosity models. Properties that might be stored with the object include: color, surface normals, texture coordinates, transparency, range data confidence, and different properties for the front and back of a polygon.

The PLY format is NOT intended to be a general scene description language, a shading language or a catch-all modeling format. This means that it includes no transformation matrices, object instantiation, modeling hierarchies, or object sub-parts. It does not include parametric patches, quadric surfaces, constructive solid geometry operations, triangle strips, polygons with holes, or texture descriptions (not to be confused with texture coordinates, which it does support!).

A typical PLY object definition is simply a list of (x,y,z) triples for vertices and a list of faces that are described by indices into the list of vertices. Most PLY files include this core information. Vertices and faces are two examples of "elements", and the bulk of a PLY file is its list of elements. Each element in a given file has a fixed number of "properties" that are specified for each element. The typical information in a PLY file contains just two elements, the (x,y,z) triples for vertices and the vertex indices for each face. Applications can create new properties that are attached to elements of an object. For example, the properties red, green and blue are commonly associated with vertex elements. New properties are added in such a way that old programs do not break when these new properties are encountered. Properties that are not understood by a program can either be carried along uninterpreted or can be discarded. In addition, one can create a new element type and define the properties associated with this element. Examples of new elements are edges, cells (lists of pointers to faces) and materials (ambient, diffuse and specular colors and coefficients). New elements can also be carried along or discarded by programs that do not understand them.


File Structure

This is the structure of a typical PLY file:

Header Vertex List Face List (lists of other elements)

The header is a series of carraige-return terminated lines of text that describe the remainder of the file. The header includes a description of each element type, including the element's name (e.g. "edge"), how many such elements are in the object, and a list of the various properties associated with the element. The header also tells whether the file is binary or ASCII. Following the header is one list of elements for each element type, presented in the order described in the header.

Below is the complete ASCII description for a cube. The header of a binary version of the same object would differ only in substituting the word "binary_little_endian" or "binary_big_endian" for the word "ascii". The comments in brackets are NOT part of the file, they are annotations to this example. Comments in files are ordinary keyword-identified lines that begin with the word "comment".

ply

format ascii 1.0 { ascii/binary, format version number }

comment made by Greg Turk { comments keyword specified, like all lines }

comment this file is a cube

element vertex 8 { define "vertex" element, 8 of them in file }

property float x { vertex contains float "x" coordinate }

property float y { y coordinate is also a vertex property }

property float z { z coordinate, too }

element face 6 { there are 6 "face" elements in the file }

property list uchar int vertex\_index { "vertex\_indices" is a list of ints }

end\_header { delimits the end of the header }

0 0 0 { start of vertex list }

0 0 1

0 1 1

0 1 0

1 0 0

1 0 1

1 1 1

1 1 0

4 0 1 2 3 { start of face list }

4 7 6 5 4

4 0 4 5 1

4 1 5 6 2

4 2 6 7 3

4 3 7 4 0

This example demonstrates the basic components of the header. Each part of the header is a carraige-return terminated ASCII string that begins with a keyword. Even the start and end of the header ("ply<cr>" and "end_header<cr>") are in this form. The characters "ply<cr>" must be the first four characters of the file, since they serve as the fileÕs magic number. Following the start of the header is the keyword "format" and a specification of either ASCII or binary format, followed by a version number. Next is the description of each of the elements in the polygon file, and within each element description is the specification of the properties. Then generic element description has this form:

element <element-name> <number-in-file>

property <data-type> <property-name-1>

property <data-type> <property-name-2>

property <data-type> <property-name-3>

...

The properties listed after an "element" line define both the data type of the property and also the order in which the property appears for each element. There are two kinds of data types a property may have: scalar and list.

Scalar data types

Here is a list of the scalar data types a property may have:
 

name double number of bytes
char  character  1
uchar unsigned character  1
short short integer 2
ushort  unsigned short integer 2
int integer  4
uint unsigned integer 4
float single-precision float 4
double double-precision float  8

These byte counts are important and must not vary across implementations in order for these files to be portable.

List data types

There is a special form of property definitions that uses the list data type:

property list <numerical-type> <numerical-type> <property-name>

An example of this is from the cube file above:

property list uchar int vertex\_index

This means that the property "vertex_index" contains first an unsigned char telling how many indices the property contains, followed by a list containing that many integers. Each integer in this variable-length list is an index to a vertex.

Another Example

Here is another cube definition:

ply

format ascii 1.0

comment author: Greg Turk

comment object: another cube

element vertex 8

property float x

property float y

property float z

property red uchar { start of vertex color }

property green uchar

property blue uchar

element face 7

property list uchar int vertex\_index { number of vertices for each face }

element edge 5 { five edges in object }

property int vertex1 { index to first vertex of edge }

property int vertex2 { index to second vertex }

property uchar red { start of edge color }

property uchar green

property uchar blue end\_header

0 0 0 255 0 0 { start of vertex list }

0 0 1 255 0 0

0 1 1 255 0 0

0 1 0 255 0 0

1 0 0 0 0 255

1 0 1 0 0 255

1 1 1 0 0 255

1 1 0 0 0 255

3 0 1 2 { start of face list, begin with a triangle }

3 0 2 3 { another triangle }

4 7 6 5 4 { now some quadrilaterals }

4 0 4 5 1

4 1 5 6 2

4 2 6 7 3

4 3 7 4 0

0 1 255 255 255 { start of edge list, begin with white edge }

1 2 255 255 255

2 3 255 255 255

3 0 255 255 255

2 0 0 0 0 { end with a single black line }

This file specifies a red, green and blue value for each vertex. To illustrate the variable-length nature of vertex_index, the first two faces of the object are triangles instead of a single square. This means that the number of faces in the object is 7. This object also contains a list of edges. Each edge contains two pointers to the vertices that delinate the edge. Each edge also has a color. The five edges defined above were specified so as to highlight the two triangles in the file. The first four edges are white, and they surround the two triangles. The final edge is black, and it is the edge that separates the triangles.


User-Defined Elements

The examples above showed the use of three elements: vertices, faces and edges. The PLY format allows users to define their own elements as well. The format for defining a new element is exactly the same as for vertices, faces and edges. Here is the section of a header that defines a material property:

element material 6
property ambient_red uchar { ambient color }
property ambient_green uchar
property ambient_blue uchar
property ambient_coeff float
property diffuse_red uchar { diffuse color }
property diffuse_green uchar
property diffuse_blue uchar
property diffuse_coeff float
property specular_red uchar { specular color }
property specular_green uchar
property specular_blue uchar
property specular_coeff float
property specular_power float { Phong power }

These lines would appear in the header directly after the specification of vertices, faces and edges. If we want each vertex to have a material specification, we might add this line to the end of the properties for a vertex:

property material\_index int

This integer is now an index into the list of materials contained in the file. It may be tempting for the author of a new application to invent several new elements to be stored in PLY files. This practice should be kept to a minimum. Much better is to try adapting common elements (vertices, faces, edges, materials) to new uses, so that other programs that understand these elements might be useful in manipulating these adapted elements. Take, for example, an application that describes molecules as collections of spheres and cylinders. It would be tempting define sphere and cylinder elements for the PLY files containing the molecules. If, however, we use the vertex and edge elements for this purpose (adding the radius property to each), we can make use of programs that manipulate and display vertices and edges. Clearly one should not create special elements for triangles and quadrilaterals, but instead use the face element. What if a program does not know the adjacency between faces and vertices (so-called unshared vertices)? This is where each triangle (say) is purely a collection of three positions in space, with no notion whether some triangles have common vertices. This is a fairly common situation. Assuming there are N triangles in a given object, then 3N vertices should be written to the file, followed by N faces that simply connect up these vertices. We anticipate that a utility will be written that converts between unshared and shared vertex files.


Object Information

Interface Routines

This section describes a set of C routines that make it easy to read and write PLY polygon files. Both binary and ASCII files can be written with almost identical procedure calls. There are simple mechanisms for allowing a program to carry along information about an object even if the program doesn't explicitly know about all the types of elements and properties in a file.

Writing Files

Whether reading or writing a PLY file, there is one data structure that is associated with a given file, and that is the "PlyFile" data type. To write a file, we call the routine "ply_write":

PlyFile *ply_write (FILE *fp, /* pointer to file for writing */
                    int nelems, /* number of elements in file */
                    char **elem_names, /* list of element names */
                    int file_type) /* binary or ascii? */

This routine returns a pointer to a structure of type PlyFile which will be used later to refer to the file. "ply_write" is called with a pointer to a file that we have opened for writing, the number of ...missing text...

General Utilities

  • rescale
  • center of mass
  • compute vertex normals
  • polygon editor
  • polygon display
  • create platonic and archemidean polyhedra
  • truncate, stellate, dual, snub
  • laplacian smoothing
  • mesh simplification
  • conversion to and from PLY files
  • shared <-> unshared vertices
  • split arbitrary polygons into triangles
  • find connected components
  • refine a subdivision surface
  • strip away some properties and/or elements of a PLY file
  • create new properties with default values
  • combine multiple polygonal objects into one
  • re-map values of properties into new ranges (like [0,255] into [0,1])
  • re-name properties
  • orient the faces of an object so that adjacent faces are consistant

Pre-Defined Elements and Properties

Although the PLY format allows arbitrary new elements and properties, the biggest benefit of using the format is for communication between programs. These programs should understand a common set of elements and properties. To that end, we present suggestions for the names and types of a number of properties.

The suggestions for properties are broken down into three separate lists. The first of these lists contain the two elements (vertex and face) and the associated four properties that ALL programs that use PLY files should understand. These four properties (x, y, z, vertex_indices) comprise the minimal information that any polygon file should contain. Writing a program that expects these four properties is trivial, thus making it easy for a program to accept any PLY file that contains these "core" properties. The second list describes further properties that are likely to be used often. The final set are some suggestions for properties that some applications may desire.

Core List (required)
Element: vertex
x float x coordinate
y float y coordinate
z float z coordinate
Element: face
vertex_indices list of int indices to vertices
Second List (often used)
Element: vertex
nx float x component of normal
ny float y component of normal
nz float z component of normal
red uchar red part of color
green uchar green part of color
blue uchar blue part of color
alpha uchar amount of transparency
material_index int index to list of materials
Element: face
Element: edge
vertex1 int index to vertex
vertex2 int other index to vertex
Element: material
red uchar red part of color
green uchar green part of color
blue uchar blue part of color
alpha uchar amount of transparency
reflect_coeff float amount of light reflected
refract_coeff float amount of light transmitted
refract_index float index of refraction
extinct_coeff float extinction coefficient
Third List (suggested extensions)
Element: vertex
face_indices list of int indices to faces
vertex_indices list of int indices to vertices
edge_indices list of int indices to edges
radius float for spheres
Element: face
back_red uchar color of backside
back_green uchar
back_blue uchar
Element: edge
face1 int index to face
face2 int other index to face
radius float for cylinders
crease_tag uchar crease in subdivision surface
Element: material
Element: cell examples: tetrahedra, cubes
face_indices list of int indices to faces
vertex_indices list of int indices to vertices
edge_indices list of int indices to edges

Copyright (c) 1994 The Board of Trustees of The Leland Stanford Junior University. All rights reserved.

Permission to use, copy, modify and distribute this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice and this permission notice appear in all copies of this software and that you do not sell the software.

THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE


Clarifications

Author: Andrew Kingdom

These are my notes, with the aim to clarify the main article, which is reproduced per above permission notice.

List data type clarification

A list is a special property that defines the format for lines of data of the same type.

There are two list formats in use:

Variable-count format:

property list <count-numerical-type> <numerical-type> <property-name>

This defines a property with a variable number of data items and is the more common usage of the two.

Example property:

property list uchar uint vertex_indices

Example data:

3 110172 110144 110158
3 110158 110428 110142

<count-numerical-type> is a count of how many indices that the property contains. Typically, it is a uchar (unsigned char, a single-digit character).

Fixed-count format:

property list <numerical-type> <property-name>

This defines a property with a fixed number of data items and is less common but is used for some types such as:

- vertex_normals
- vertex_colors
- face_indices
- texture_coordinates

The count for this format is established by convention, based on the name. The expectated number of items should be validated against the actual data count.

For more details, see:

(C) 2023 Andrew Kingdom, All rights reserved. MIT License

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment