Every PLY file starts with a text header, whatever the encoding of its data. The header names the encoding, then declares each kind of element the file holds, how many there are, and the properties each one carries. A reader needs nothing else to find its way through the data that follows.
Header lines
The header is a series of lines of ASCII text, each starting with a keyword. Keywords
and type names are lower case. Words on a line are separated by spaces. Greg Turk’s
description calls the lines “carriage-return terminated”; in practice files end each
line with a line feed (0A), and some readers, RPly for one, also accept a
carriage return and line feed when the first line is written that way.
| Line | Where | Meaning |
|---|---|---|
ply | First line, exactly | The magic number: the bytes 70 6C 79 and the line end |
format encoding version | Second line | How the data after the header is written |
comment text | Any line after the first | A comment; the rest of the line is free text |
obj_info text | Any line after the first | Information about the object as a whole, as free text |
element name count | Any number, in data order | Starts the declaration of one kind of element, and how many items of it follow |
property type name | After an element line | One scalar value that each item of that element carries |
property list count-type item-type name | After an element line | A variable-length list that each item carries |
end_header | Last line | Ends the header; the data starts on the next byte |
format – Data encoding
The format line names one of three encodings and the version of the format. The only
version ever defined is 1.0.
| Encoding | Data |
|---|---|
ascii | Numbers written as text, separated by white space |
binary_little_endian | Packed binary values, least significant byte first |
binary_big_endian | Packed binary values, most significant byte first |
The header is the same text in all three. Data encodings describes each one.
comment and obj_info
A comment line holds any text, which readers ignore. Turk’s examples use
them for the author and the object’s name.
An obj_info line holds information about the object as a whole, also as
free text. It is not in Turk’s description, but libraries such as RPly read and write
it. Both kinds of line are optional, and their order relative to each other does not
matter.
element – Declaring elements
An element line gives a name and a count. Every property line
after it, up to the next element line or end_header, belongs
to that element. The data holds the elements in the same order as the header, all the
items of the first element, then all the items of the second, and so on.
Element and property names are chosen by the writer. A reader looks for the names it knows and skips the rest, which it can do because the header gives the size and type of everything. Turk’s description asks writers to reuse the usual elements and property names below, rather than invent new ones, so that other programs can read the data.
Scalar types
Each property has a type. Two sets of names are in use: the original names, and the names with sizes that later writers added. Readers should accept both. The sizes must not vary between implementations, so that files stay portable.
| Name | Also written | Size | Meaning |
|---|---|---|---|
char | int8 | 1 | Signed integer |
uchar | uint8 | 1 | Unsigned integer |
short | int16 | 2 | Signed integer |
ushort | uint16 | 2 | Unsigned integer |
int | int32 | 4 | Signed integer |
uint | uint32 | 4 | Unsigned integer |
float | float32 | 4 | IEEE 754 single-precision floating point |
double | float64 | 8 | IEEE 754 double-precision floating point |
Signed integers are two’s complement.
property list – Lists
A list property carries a variable number of values. Its declaration gives two types: the type of the count, then the type of each item. In the data, each list is its count followed by that many items.
element face 2
property list uchar int vertex_index
end_header
3 0 1 2
4 0 2 3 4
The count type must be an integer type; uchar is usual, which limits a
list to 255 items. Vertex numbers in a face count from 0, in the order the vertices
appear in the data. Turk’s description states that a face’s vertices make a triangle
fan, not a triangle strip: a face with vertices a b c d is the triangles
a b c and a c d.
The usual elements
These elements and properties appear in Turk’s description and its examples.
| Element | Property | Type in the examples | Meaning |
|---|---|---|---|
vertex | x, y, z | float | Position |
vertex | red, green, blue | uchar | Colour, 0 to 255 |
face | vertex_index | list uchar int | The face’s vertex numbers |
edge | vertex1, vertex2 | int | The vertex numbers at the two ends |
edge | red, green, blue | uchar | Colour of the edge |
material | ambient_red, ambient_green, ambient_blue | uchar | Ambient colour |
material | ambient_coeff | float | Ambient coefficient |
material | diffuse_red, diffuse_green, diffuse_blue | uchar | Diffuse colour |
material | diffuse_coeff | float | Diffuse coefficient |
material | specular_red, specular_green, specular_blue | uchar | Specular colour |
material | specular_coeff | float | Specular coefficient |
material | specular_power | float | Specular exponent |
Many programs write the face list as vertex_indices instead of
vertex_index, so readers usually accept both. Other names are conventions
of later programs rather than part of the description: for example nx,
ny and nz for a vertex normal, alpha for opacity,
and s and t or u and v for texture
coordinates. A point cloud is simply a file with a vertex element and no
face element.
A complete header
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 uchar red
property uchar green
property uchar blue
element face 7
property list uchar int vertex_index
element edge 5
property int vertex1
property int vertex2
property uchar red
property uchar green
property uchar blue
end_header