PRT File Format Documentation

We’ve created documentation for the PRT file format. Here’s the page, if you have any comments about it, like maybe it needs clarifying in some parts, let us know.


-Mark

The Particle file (.PRT) format is the standard file format used in Krakatoa, which best supports all the options and capabilities that Krakatoa has to offer. The file format allows for the use of an arbitrary number of channels, each with a customizable type and name.

The file format is designed so the header is uncompressed while the particle data is compressed. This provides the ability to efficiently write a PRT file without knowing ahead of time how many particles will be written, by seeking back into the header and updating it with the correct particle count once all the particles have been written. When doing this, it is recommended that the value -1 be written to the particle count initially, so that Krakatoa and other programs can detect it as an error condition when loading the file.

Channel names are restricted, and should start with a letter or '_' character, and contain only letters, numbers and '_'. This restriction is imposed to provide for the possibility of implementing scripting support in Krakatoa and other systems where the channel names are directly used in the language to access those channels. Consider, for example, what a Krakatoa shading language would be like. Channel names are case sensitive.

For Krakatoa to work with a PRT file, the file must at minimum contain a Position channel which contains 3 values of a floating point type.

Format Specification

The PRT file format consists of:

  • A header for general file information.
  • A channel definitions section, detailing the channel-wise data included in each particle.
  • A block of binary data for the particles. This is compressed using zlib's deflate method.

All data is in little-endian byte order.

PRT File Header

(56 bytes)

(8 bytes)  Magic number that indicates the PRT file format. The number is defined by the following sequence of ASCII characters: 
           {192, 'P', 'R', 'T', '\r', '\n', 26, '\n'}
(4 bytes)  A 32 bit int indicating the length of the header (Has value 56).
(32 bytes) A human readable signature null-terminated string describing the file, currently "Extensible Particle Format"
(4 bytes)  A 32 bit int indicating version (Has value 1).
(8 bytes)  A 64 bit int indicating particle count

Reserved Bytes

(4 bytes)

(4 bytes) A 32 bit int, should be set to the value 4.

Channels Definition Section

(Variable Length)

Header (8 bytes)

(4 bytes)  A 32 bit int indicating the number of channels.
(4 bytes)  A 32 bit int indicating the length of one channel definition structure (Has value 44).

Channel definitions (44 bytes each)

(32 bytes) A null-terminated string indicating the channel name.  Must match the regex "[a-zA-Z_][0-9a-zA-Z_]*".
(4 bytes)  A 32 bit int indicating channel arity (number of data values that each particle has for this channel).
(4 bytes)  A 32 bit int indicating channel type.  Supported channel types are indicated in the table below.
(4 bytes)  A 32 bit int indicating channel offset, relative to the start of the particle.
Data Type Integer Value
int16 0
int32 1
int64 2
float16 3
float32 4
float64 5
uint16 6
uint32 7
uint64 8
int8 9
uint8 10

Particle Data

(Variable Size)

The particle data is compressed using a zlib z_streamp object, with the basic zlib deflate API. In Krakatoa, this is implemented with the deflateInit, deflate, and deflateEnd functions for compression, and the inflateInit, inflate, and inflateEnd functions for decompression.

The particles are byte-packed one after another, in the layout specified by the channels definition section.

The float16 data type is the same as the half data type in OpenEXR. The most convenient way to work with them is using the half library component from OpenEXR.

Example

Let's say you want to write 10 particles to a PRT file, and that each particle will have position and velocity information associated with it.

The following values will need to be written for the header information.

{192, 'P', 'R', 'T', '\r', '\n', 26, '\n'}  // magic number
56                                          // header length
Extensible Particle Format                  // identification string
1                                           // version 1
10                                          // particle count
4                                           // reserved byte value
2                                           // number of channels
44                                          // channel definition length
Position                                    // channel name
4                                           // data type - float32
3                                           // triplet of values for 3D position 
0                                           // first data channel in the particle, no offset
Velocity                                    // channel name 
4                                           // data type - float32
3                                           // triplet of values for 3D velocity vector)
12                                          // offset 4*3=12 bytes to get past the position triplet

Following the header information, the channel-wise information will be compressed via zlib and written to the file. The pre-packed information will consist of 24 bytes per particle, 12 for the position triplet and 12 for the velocity triplet:

[float32][float32][float32][float32][float32][float32][float32][float32][float32][float32][float32][float32]...
|_________________________||_________________________||_________________________||_________________________|
         position                    velocity                  position                    velocity
|____________________________________________________||____________________________________________________|
                      particle 1                                            particle 2

As mentioned above, it is recommended that the value -1 be written to the particle count initially, so that Krakatoa and other programs can detect it as an error condition when loading the file. Once the per particle information has been packed into the file, the particle count in the header can then be updated to the true value.

Some channels are pre-defined, though optional, right? Like mapping channels, density, etc. What names should those be given in the header?


  • Chad

Good question. This also applies to CSV files, as there is a 1-1 mapping between the types in the PRT format and what can be specified in the column header line of a CSV file. Here are most of the channel names that Krakatoa uses currently:



Position - particle position

Velocity - particle velocity

Normal - particle normal

Density - particle density

Color - particle color (converts to/from map channel 0 in max where appropriate)

TextureCoord - texture coordinates (converts to/from map channel 1 in max where appropriate)

Mapping2, Mapping3, … - The rest of the 3ds Max map channels



There are a few more channels that get read from particle flow which you can save out to CSV or PRT, but aren’t used by Krakatoa yet.



-Mark

Ah, so exactly as they show up in the Channels To Save rollout? Age, ID, Orientation, etc?


  • Chad

Ah, so exactly as they show up

in the Channels To Save

rollout? Age, ID,

Orientation, etc?



Yes, whatever is shown in the GUI is saved as a comma-delimited string in the Renderer and is being used (if supported) by that name.



For example, type in the MAXScript Listener



FranticParticles.getProperty “ActiveParticleChannels”

“Position,float32,3,Velocity,float16,3,Color,float16,3,Density,float16,1,Normal,float16,3”



FranticParticles.getProperty “InactiveParticleChannels”

“Age,int32,1,ID,int32,1,Orientation,float32,4,TextureCoord,float16,3”



(these are the defaults)

Each string contains the name, the type and the arity of each property. The names are written as they are recognized by Krakatoa. Channels like Age are currently not used but we were preparing for future versions ;o)