Difference between revisions of "POF data structure"

From FreeSpace Wiki
Jump to: navigation, search
(Chunk specs)
(Chunk specs)
 
(9 intermediate revisions by 2 users not shown)
Line 37: Line 37:
 
Notes:
 
Notes:
  
*PSPO stood for Parallax Software Polygon Object at one time. Conflict, Descent: FreeSpace shipped at POF version 20.14, so version would be equal to 2014 in most POF files (not all). FreeSpace 2 shipped at POF version 21.17, some files are 21.16.
+
*PSPO stood for Parallax Software Polygon Object at one time. Conflict, Descent: FreeSpace shipped at POF version 20.14, so version would be equal to 2014 in most POF files (not all). FreeSpace 2 shipped at POF version 21.17, some files are 21.16. POF version 2118 was added on December 2019, this version ensures data alignment and SLC2 chunk presence.
 
   
 
   
  
Line 45: Line 45:
 
int length        // length of the chunk.
 
int length        // length of the chunk.
 
</pre>
 
</pre>
 +
 +
Note: Contrary to BSP_Data structure, in pof data structure "Length" does not considers the first 8 bytes of the chunk (chunk id+length), so in order to go from the begining of the chunk to the next one you need to do lenght+8.
 +
[[Media:Pof_chunk.png]]
  
 
==Chunk specs==
 
==Chunk specs==
Line 112: Line 115:
 
int num_textures
 
int num_textures
 
for each texture,i
 
for each texture,i
 +
int tex_filename_length[i]
 
  string tex_filename[i]    // texture filename
 
  string tex_filename[i]    // texture filename
 
</pre>
 
</pre>
Line 124: Line 128:
 
int num_paths
 
int num_paths
 
for each path, p {
 
for each path, p {
 +
int name_length
 
  string name[p]            // name
 
  string name[p]            // name
 +
int parent_length
 
  string parent[p]          // parent's name
 
  string parent[p]          // parent's name
 
  int num_verts[p]
 
  int num_verts[p]
Line 143: Line 149:
 
int num_special_points
 
int num_special_points
 
for each special_point, p {
 
for each special_point, p {
 +
int name_length[p]
 
  string name[p]
 
  string name[p]
 +
int properties_length[p]
 
  string properties[p]
 
  string properties[p]
 
  vector point[p]
 
  vector point[p]
Line 226: Line 234:
  
 
for each dock, d {
 
for each dock, d {
 +
int properties_length
 
  string properties[d]    // see notes below
 
  string properties[d]    // see notes below
 
  int num_spline_paths[d]
 
  int num_spline_paths[d]
Line 251: Line 260:
 
   
 
   
 
  #ifdef version2117orhigher // FreeSpace 2 change
 
  #ifdef version2117orhigher // FreeSpace 2 change
 +
  int properties_length
 
   string properties
 
   string properties
 
  #endif
 
  #endif
Line 283: Line 293:
 
vector bounding_box_max_point
 
vector bounding_box_max_point
  
 +
int submodel_name_length
 
string submodel_name
 
string submodel_name
 +
int properites_length
 
string properites
 
string properites
 
int movement_type
 
int movement_type
Line 330: Line 342:
  
 
<pre>
 
<pre>
int num_glowpoints
+
int num_glowbanks
for each glowpoint, g {
+
for each glowbank, g {
 
  int disp_time // displacement time for blinking (e.g. for Orion runway lights)
 
  int disp_time // displacement time for blinking (e.g. for Orion runway lights)
 
  int on_time
 
  int on_time
Line 338: Line 350:
 
  int LOD // Should be 0
 
  int LOD // Should be 0
 
  int type // Should also be 0
 
  int type // Should also be 0
 +
int num_glowpoints // Number of glowpoints in this bank
 +
int properties_length
 
  string properties // Texture name, no file path or extension, like: "$glow_texture=..."
 
  string properties // Texture name, no file path or extension, like: "$glow_texture=..."
  vector point
+
  for each glowpoint, p {
vector norm // (0,0,0) is omnidirectional
+
  vector point // location vector
float radius
+
  vector norm // (0,0,0) is omnidirectional
 +
  float radius
 +
}
 
}
 
}
 
</pre>
 
</pre>
Line 370: Line 386:
  
 
** The shield mesh collision tree speeds up collision calculation by adding bounding boxes around each polygon in the shield mesh.  It works basically like a [[bsp_data_structure|BSP tree]].
 
** The shield mesh collision tree speeds up collision calculation by adding bounding boxes around each polygon in the shield mesh.  It works basically like a [[bsp_data_structure|BSP tree]].
 +
** The first byte of each node is a size 1 char, this makes all the following data to be in unaligned memory access positions, thus making the SLDC system, as it is, incompatible with ARM arch.
 +
 +
*'SLC2' - Revised version of Shield mesh collision tree info (FS2_Open and POF version 2118 only)
  
 +
<pre>
 +
uint tree_size
 +
for each node, n {
 +
int type // 0 = SPLIT, 1 = LEAF/polylist
 +
uint size
 +
if !type { // SPLIT
 +
  vector bound_min
 +
  vector bound_max
 +
  uint front_offset
 +
  uint back_offset
 +
}
 +
else { // LEAF
 +
  vector bound_min
 +
  vector bound_max
 +
  uint num_polygons
 +
  for each polygon, p {
 +
  uint polygon_addr // indexed into shield mesh face list?
 +
  }
 +
}
 +
}
 +
</pre>
 +
 +
** The shield mesh collision tree speeds up collision calculation by adding bounding boxes around each polygon in the shield mesh.  It works basically like a [[bsp_data_structure|BSP tree]].
 +
** Revised version of SLDC chunk, now with a int for type to ensure data alignment.
  
 
[[Category:File Types]]
 
[[Category:File Types]]

Latest revision as of 19:10, 16 December 2019

The following information is copied from the Descent Developer's Network pof specs page.

Introduction

These files are used to hold the ship model data. The data for the ship is basically a BSP tree that doesn't split polygons across the planes (since we're using zbuffering) and the planes are created by halving the largest dimension of each bounding box recursively. Due to the vastness of some of our models, this recursively split structure allows our collision detection and dynamic lighting to do quick out's on the model. For example, to check if a vector interects the model, I check against the bounding box. If it hits, I then check each of the two boxes it is split into. Then I recursively check each subdivided box as long as the vector is still interesecting.

We create the POF file by exporting a 3DS Max file with a special plugin tool to generate a ".P3D" file, which is essentially a 3d studio file only with texture uv's stored for each face vertex rather than for each vertex. We then run the .P3D file through an in-house Win32 program called 'BSPGEN' which creates the POF file. Depending on the model size, BSPGEN takes a few minutes or so.

Each ship is made up of a lot of subobjects where a subobject is a collection of polygons. For instance, each detail level of a hull of a ship is a subobject. A radar dish is a subobject. A piece of debris that the ship explodes into is a subobject. Each subobject can then have children subobjects. To draw the highest detail model of a ship, draw the subobject identified as detail level 0 and draw all it's children.

This document was written by John Slagel from Volition Inc., with revisions and bugfixing from Garry Knudson. FreeSpace 2 additions were made by Dave Baranec (Volition Inc.), Garry Knudson and Francis "Pastel" Avila.

POF block order list for retail FS2 models

Data types

This uses standard Intel data types.

  (int) == 4 bytes, signed
  (uint) == 4 bytes, unsigned
  (short) == 2 bytes, signed
  (ushort) == 2 bytes, unsigned
  (char) == 1 byte, signed
  (ubyte) == 1 byte, unsigned
  (float) == 4 bytes, signed
  (vector) == 3 floats, 12 bytes total
  (string) == an int specifying length of string and char[length] for the string itself
  (note: it is NOT null-terminated)

Basic file structure

The file format is a binary file using standard Intel data types. The header consists of a signature and the file version number:

char[4] file_id   // must be 'PSPO'
int version       // Major*100+Minor

Notes:

  • PSPO stood for Parallax Software Polygon Object at one time. Conflict, Descent: FreeSpace shipped at POF version 20.14, so version would be equal to 2014 in most POF files (not all). FreeSpace 2 shipped at POF version 21.17, some files are 21.16. POF version 2118 was added on December 2019, this version ensures data alignment and SLC2 chunk presence.


  • The rest of the file is a bunch of chunks. Each chunk is:
char[4] chunk_id  // see below for available chunk types
int length        // length of the chunk.

Note: Contrary to BSP_Data structure, in pof data structure "Length" does not considers the first 8 bytes of the chunk (chunk id+length), so in order to go from the begining of the chunk to the next one you need to do lenght+8. Media:Pof_chunk.png

Chunk specs

Here is a breakdown of each of the chunk types:

  • 'OHDR' (FreeSpace 1) and 'HDR2' (FreeSpace 2) - Object header info
#ifdef version2116orhigher
 // FreeSpace 2
 float max_radius           // maximum radius of entire ship
 int obj_flags              // object flags. Bit 1 = Textures contain tiling
 int num_subobjects         // number of subobjects
#else
 // FreeSpace 1
 int num_subobjects         // number of subobjects
 float max_radius           // maximum radius of entire ship
 int obj_flags              // object flags. Bit 1 = Textures contain tiling
#endif

vector min_bounding         // min bounding box point
vector max_bounding         // max bounding box point

int num_detaillevels        // number of detail levels
for each detail_level, i {
 int sobj_detailevels[i]    // subobject number for detail level I, 0 being highest.
}

int num_debris              // number of debris pieces that model explodes into
for each debris piece, i
 int sobj_debris[i]         // subobject number for debris piece i

#ifdef version1903orhigher
 float mass                 // see notes below
 vector mass_center         // center of mass
 float[3][3] moment_inertia // moment of inertia
#endif

#ifdef version2014orhigher
 int num_cross_sections     // number of cross sections (used for exploding ship) (*)
 for each cross_section, i {
  float depth[i]
  float radius[i]
 }
#endif

#ifdef version2007orhigher
 int num_lights             // number of precalculated muzzle flash lights
 for each light {
  vector location
  int light_type            // type of light
 }
#endif
    • Notes:

for version<2009, mass is a volume mass for version>=2009, mass is an area mass conversion: area_mass=4.65*(vol_mass^2/3); also scale moment_inertia by vol_mass/area_mass (*) if there is no cross_section data, num_cross_sections is -1 instead of 0, as one would expect.


  • 'TXTR' - A list of textures used on this ship. The order they appear here is the number that a face uses to reference a particular texture.
int num_textures
for each texture,i
 int tex_filename_length[i] 
 string tex_filename[i]    // texture filename
  • 'PINF' - Miscellaneous info about the POF file, command line, etc.

Contains a block of NULL-terminated strings. Just read chunk size bytes and stuff it into a string.

  • 'PATH' - Paths for docking and AI ships to follow
int num_paths
for each path, p {
 int name_length
 string name[p]             // name
 int parent_length
 string parent[p]           // parent's name
 int num_verts[p]
 for each vert, v {
   vector pos[p,v]
   float radius[p,v]
   int num_turrets[p,v]
   for each turret, t {
    int sobj_number[p,v,t]  // subobject number
   }
 }
}
  • 'SPCL' - Data for special points
int num_special_points
for each special_point, p {
 int name_length[p]
 string name[p]
 int properties_length[p]
 string properties[p]
 vector point[p]
 float radius[p]
}
  • 'SHLD' - Data for the shield mesh
int num_vertices
for each vertex, v {
 vector position[v]
}

int num faces
for each face, f {
 vector face_normal[f]
 int[3] face_vertices    // indexed into vertex list
 int[3] neighbors        // indexed into face list
}


  • ' EYE' - Data for eye points (Where pilot looks in 1st person views). Note the space in front of EYE.
int num_eye_positions
for each eye_position, e {
 int sobj_number[e]      // subobject number this eye is attached to
 vector sobj_offset[e]   // offset from subobject
 vector normal[e]
}
  • 'GPNT' and 'MPNT' - Gun and Missile firing points
int num_slots
for each slot, s {
 int num_guns[s]
 for each gun, g {
   vector point[g,s]
   vector norm[g,s]
 }
}
    • Notes:

A "slot" is what you see in the loadout screen. Primaries have a max of 2 and secondaries of 3 for player-flyable ships. "Guns" are the actual number of barrels and hence projectiles you'll get when you press the trigger. There is likely no practical max.


  • 'TGUN' and 'TMIS' - Turret Gun and Turret Missile firing points.
int num_banks

for each bank, b {
 int sobj_parent[b]       // parent subobject (the subobject with which this turret is associated) (*)
 int sobj_par_phys[b]     // physical parent subobject (the subobject to which the turret rotates) (*)

 vector turret_normal[b]
 int num_firing_points[b]
 for each firing_point {
  vector position[b,f]
 }
}
    • Notes:

For multipart turrets, sobj_parent is the "barrel" of a turret, and the firing points will be in this sobj's axial frame. sobj_par_phys is the "base" of a turret, which the barrel will rotate with. For single-part turrets, sobj_parent == sobj_parent_phys.


  • 'DOCK' - Data for docking points
int num_docks

for each dock, d {
 int properties_length
 string properties[d]     // see notes below
 int num_spline_paths[d]
 for each spline_path, p {
  int path_number[d,p]
 }
 
 int num_points
 for each point, d {
  vector position
  vector normal
 }
}
    • Note: Properties… if $name= found, then this is name. If name is cargo then this is a cargo bay.


  • 'FUEL' - Data for engine thruster glows
int num_thrusters
for each thruster,t {
 int num glows[t]
 
 #ifdef version2117orhigher // FreeSpace 2 change
  int properties_length
  string properties
 #endif
 
 for each glow {
  vector pos[t,g]
  vector norm[t,g]   // used to tell if behind glow
  float radius[t,g]
 }
}
  • 'SOBJ' (FreeSpace 1) and 'OBJ2' (FreeSpace 2) - Data for a subobject. Contains some info and a bunch of vertices and polygons in the form of a BSP tree or Octree depending on how you look at it.
int submodel_number  // What submodel number this is.

#ifdef version2116orhigher
 // FreeSpace 2
 float radius        // radius of this subobject
 int submodel_parent // What submodel is this model's parent. Equal to -1 if none.
 vector offset       // Offset to from parent object <- Added 09/10/98
#else
 // FreeSpace 1
 int submodel_parent // What submodel is this model's parent. Equal to -1 if none.
 vector offset       // Offset to from parent object <- Added 09/10/98
 float radius        // radius of this subobject
#endif

vector geometric_center
vector bounding_box_min_point
vector bounding_box_max_point

int submodel_name_length
string submodel_name
int properites_length
string properites
int movement_type
int movement_axis

int reserved         // must be 0
int bsp_data_size    // number of bytes now following
char[bsp_data_size] bsp_data  // contains actual polygons, etc.
    • Note: bsp_data is explained here


  • 'INSG' - Squad logo/Insignia data chunk (FreeSpace 2 only)
int num_insignias
for each insignia, i {
 int detail_level // ship detail level
 int num_faces
 int num_vertices
 for each vertice, j {
  vector vertex_position
 }

 vector offset // offset of the insignia in model coords
 for each face, j {
  for 0 to 2, k {
   int vertex_index // vertex index for this face
   float u_texture_coordinate
   float v_texture_coordinate
  }
 }
}


  • 'ACEN' - Auto-Centering info (FreeSpace 2 only)
vector point // autocentering point for the entire model
    • The autocentering point was basically just a little convenient extra data we stuck in models where the pivot point of the model wasn't at the center. Since we rotate ships in the tech room by rotating around the pivot point, it looked dumb when the colossus' rear end was in the middle of the screen and it was spinning on it. So the autocenter point was just a point pretty much near the model we could use to push an extra matrix onto our transform stack and have it show up centered around it. The point itself is in model coordinates.
  • 'GLOW' - Glowpoint info (FS2_Open only)
int num_glowbanks
for each glowbank, g {
 int disp_time // displacement time for blinking (e.g. for Orion runway lights)
 int on_time
 int off_time
 int obj_parent // parent subobject number
 int LOD // Should be 0
 int type // Should also be 0
 int num_glowpoints // Number of glowpoints in this bank
 int properties_length
 string properties // Texture name, no file path or extension, like: "$glow_texture=..."
 for each glowpoint, p {
  vector point // location vector
  vector norm // (0,0,0) is omnidirectional
  float radius
 }
}
  • 'SLDC' - Shield mesh collision tree info (FS2_Open only)
uint tree_size
for each node, n {
 ubyte type // 0 = SPLIT, 1 = LEAF/polylist
 uint size
 if !type { // SPLIT
  vector bound_min
  vector bound_max
  uint front_offset
  uint back_offset
 }
 else { // LEAF
  vector bound_min
  vector bound_max
  uint num_polygons
  for each polygon, p {
   uint polygon_addr // indexed into shield mesh face list?
  }
 }
}
    • The shield mesh collision tree speeds up collision calculation by adding bounding boxes around each polygon in the shield mesh. It works basically like a BSP tree.
    • The first byte of each node is a size 1 char, this makes all the following data to be in unaligned memory access positions, thus making the SLDC system, as it is, incompatible with ARM arch.
  • 'SLC2' - Revised version of Shield mesh collision tree info (FS2_Open and POF version 2118 only)
uint tree_size
for each node, n {
 int type // 0 = SPLIT, 1 = LEAF/polylist
 uint size
 if !type { // SPLIT
  vector bound_min
  vector bound_max
  uint front_offset
  uint back_offset
 }
 else { // LEAF
  vector bound_min
  vector bound_max
  uint num_polygons
  for each polygon, p {
   uint polygon_addr // indexed into shield mesh face list?
  }
 }
}
    • The shield mesh collision tree speeds up collision calculation by adding bounding boxes around each polygon in the shield mesh. It works basically like a BSP tree.
    • Revised version of SLDC chunk, now with a int for type to ensure data alignment.