PHY: Difference between revisions
mNo edit summary |
mNo edit summary |
||
(23 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
[[PHY]] is the extension for | [[PHY]] is the extension for {{source|4}}'s proprietary [[collision model]] data format. It stores data for the rigid or jointed (ragdoll) [[collision model]] used by the [[MDL]]. In addition, it stores some [[$keyvalues]] about a model such as which gib-models to use if destroyed. | ||
{{note|The PHY file is completely independent from the rest of the model's binaries, such as MDL or VVD. As long as the filename and skeleton structure matches the data within the MDL file, the collision data can be changed without any adverse affects. A collision mesh could be added to a model without one initially, but a recompile would be needed to set the correct flags within the MDL file and to add [[$keyvalues]] data.}} | |||
Also if you're optimizing your map because you have too many edicts it's useful to know that the PHY file counts as one edict (adds to Entity_limit) for unknown reasons. You can delete the PHY file to overcome this problem if the custom model doesn't need the collision data. | |||
==File Format== | |||
In brief, a PHY file is formed by: | In brief, a PHY file is formed by: | ||
* A main header, which specifies the number of solids | * A main header, which specifies the number of solids | ||
* A series of VPHY/collision | * A series of VPHY/collision metadata sections, one per solid | ||
* A | * A collection of triangles and vertices composing the collider meshes | ||
* A single text data section, variously-ordered block elements that specify which solid they apply to | |||
=== Main header === | ===Main header=== | ||
The first bytes of the file form the main header: | |||
<source lang="cpp"> | |||
// this structure can be found in <mod folder>/src/public/phyfile.h | |||
typedef struct phyheader_s | |||
{ | |||
DECLARE_BYTESWAP_DATADESC(); | |||
int size; // Size of this header section (generally 16) | |||
int id; // Often zero, unknown purpose. | |||
int solidCount; // Number of solids in file | |||
long checkSum; // checksum of source .mdl file (32 bits) | |||
} phyheader_t;</source> | |||
===Collision data sections=== | |||
There will be a series of these sections, back-to-back, numbering the same as the header's <code>solidCount</code>. | |||
<source lang="cpp"> | |||
// new phy format | |||
struct compactsurfaceheader_t | |||
{ | |||
int size; // Size of the content after this byte | |||
int vphysicsID; // Generally the ASCII for "VPHY" in newer files | |||
short version; | |||
short modelType; | |||
int surfaceSize; | |||
Vector dragAxisAreas; | |||
int axisMapSize; | |||
}; | |||
// old style phy format | |||
struct legacysurfaceheader_t | |||
{ | { | ||
int | int size; | ||
int | float mass_center[3]; | ||
int | float rotation_inertia[3]; | ||
float upper_limit_radius; | |||
}</source> | int max_deviation : 8; | ||
int byte_size : 24; | |||
int offset_ledgetree_root; | |||
int dummy[3]; // dummy[2] is "IVPS" or 0 | |||
}; | |||
</source> | |||
{{note|Probably it's not complete description of solid section }} | |||
Each surface has a list of convex solids that ends at the <code>vertices_offset</code> address. | |||
{ | <source lang="cpp"> | ||
struct convexsolidheader_t { | |||
int vertices_offset; // For each convexsolidheader_t it will point to the same address of the file | |||
int bone_index; | |||
int flags; | |||
int triangles_count; | |||
} | |||
</source> | |||
After the convex solid data there's a list of triangles: | |||
<source lang="cpp"> | |||
struct triangledata_t { | |||
byte vertex_index; // can be used to calculate the number of vertices used below | |||
byte unused1; | |||
unsigned short unused2; | |||
short vertex1_index; | |||
short unused3; | |||
short vertex2_index; | |||
short unused4; | |||
short vertex3_index; | |||
short unused5; | |||
} | |||
</source> | |||
{ | From the <code> vertices_offset </code> address comes the full list of vertices in <code> vec4 </code> | ||
<source lang="cpp"> | |||
struct phyvertex_t | |||
{ | |||
Vector3 pos; // relative to bone | |||
float unknown; | |||
}; | |||
</source> | |||
<source lang="cpp"> | |||
struct phynode_t | |||
{ | |||
int rightnodeindex; | |||
int convexindex; | |||
Vector3 center; | |||
float radius; | |||
int bboxsize; // volume? | |||
}; | |||
</source> | |||
===Text section=== | |||
The text section occurs near the end of the file, and is one single string with no size header. Among the text blocks it may contain: | |||
* <code>solid</code> blocks define solids, using an <code>index</code> value to distinguish which set of collision data they refer to. | |||
* <code>ragdollconstraint</code> blocks define relationships between <code>parent</code> and <code>child</code> solids. | |||
* An <code>editparams</code> block with general information about the entire object. | |||
* Various <code>break</code> blocks with information about rigid or jointed gibs created when the model is destroyed. | |||
Here is an ''incomplete'' sample from the Combine Strider, formatted for readability. | |||
<source lang="text"> | |||
solid | |||
{ | |||
"index" "8" | |||
"name" "Combine_Strider.Leg_Hind_Bone1" | |||
"parent" "Combine_Strider.Leg_Hind_Bone" | |||
"mass" "137.739014" | |||
"surfaceprop" "strider" | |||
"damping" "0.000000" | |||
"rotdamping" "1.000000" | |||
"inertia" "1.000000" | |||
"volume" "109397.476563" | |||
} | |||
ragdollconstraint | |||
{ | |||
"parent" "7" | |||
"child" "8" | |||
"xmin" "0.000000" | |||
"xmax" "0.000000" | |||
"xfriction" "2.000000" | |||
"ymin" "0.000000" | |||
"ymax" "0.000000" | |||
"yfriction" "2.000000" | |||
"zmin" "-32.000000" | |||
"zmax" "138.000000" | |||
"zfriction" "0.200000" | |||
} | |||
editparams | |||
{ | |||
"rootname" "combine_strider.body_bone" | |||
"totalmass" "2000.000000" | |||
} | |||
break | |||
{ | |||
"model" "Gibs/Strider_Gib1" | |||
"health" "0" | |||
"fadetime" "0" | |||
"placementbone" "Combine_strider.body_bone" | |||
} | |||
break | |||
{ | |||
"ragdoll" "Gibs/Strider_Head" | |||
"health" "0" | |||
"fadetime" "0" | |||
} | |||
</source> | |||
== See also == | ==See also== | ||
* [[Model Creation Overview]] | * [[Model Creation Overview]] | ||
[[Category: | [[Category:Source]] | ||
[[Category:File formats]] | |||
[[Category:Modeling]] | [[Category:Modeling]] |
Latest revision as of 08:36, 6 February 2025
PHY is the extension for Source's proprietary collision model data format. It stores data for the rigid or jointed (ragdoll) collision model used by the MDL. In addition, it stores some $keyvalues about a model such as which gib-models to use if destroyed.

Also if you're optimizing your map because you have too many edicts it's useful to know that the PHY file counts as one edict (adds to Entity_limit) for unknown reasons. You can delete the PHY file to overcome this problem if the custom model doesn't need the collision data.
File Format
In brief, a PHY file is formed by:
- A main header, which specifies the number of solids
- A series of VPHY/collision metadata sections, one per solid
- A collection of triangles and vertices composing the collider meshes
- A single text data section, variously-ordered block elements that specify which solid they apply to
Main header
The first bytes of the file form the main header:
// this structure can be found in <mod folder>/src/public/phyfile.h
typedef struct phyheader_s
{
DECLARE_BYTESWAP_DATADESC();
int size; // Size of this header section (generally 16)
int id; // Often zero, unknown purpose.
int solidCount; // Number of solids in file
long checkSum; // checksum of source .mdl file (32 bits)
} phyheader_t;
Collision data sections
There will be a series of these sections, back-to-back, numbering the same as the header's solidCount
.
// new phy format
struct compactsurfaceheader_t
{
int size; // Size of the content after this byte
int vphysicsID; // Generally the ASCII for "VPHY" in newer files
short version;
short modelType;
int surfaceSize;
Vector dragAxisAreas;
int axisMapSize;
};
// old style phy format
struct legacysurfaceheader_t
{
int size;
float mass_center[3];
float rotation_inertia[3];
float upper_limit_radius;
int max_deviation : 8;
int byte_size : 24;
int offset_ledgetree_root;
int dummy[3]; // dummy[2] is "IVPS" or 0
};

Each surface has a list of convex solids that ends at the vertices_offset
address.
struct convexsolidheader_t {
int vertices_offset; // For each convexsolidheader_t it will point to the same address of the file
int bone_index;
int flags;
int triangles_count;
}
After the convex solid data there's a list of triangles:
struct triangledata_t {
byte vertex_index; // can be used to calculate the number of vertices used below
byte unused1;
unsigned short unused2;
short vertex1_index;
short unused3;
short vertex2_index;
short unused4;
short vertex3_index;
short unused5;
}
From the vertices_offset
address comes the full list of vertices in vec4
struct phyvertex_t
{
Vector3 pos; // relative to bone
float unknown;
};
struct phynode_t
{
int rightnodeindex;
int convexindex;
Vector3 center;
float radius;
int bboxsize; // volume?
};
Text section
The text section occurs near the end of the file, and is one single string with no size header. Among the text blocks it may contain:
solid
blocks define solids, using anindex
value to distinguish which set of collision data they refer to.ragdollconstraint
blocks define relationships betweenparent
andchild
solids.- An
editparams
block with general information about the entire object. - Various
break
blocks with information about rigid or jointed gibs created when the model is destroyed.
Here is an incomplete sample from the Combine Strider, formatted for readability.
solid
{
"index" "8"
"name" "Combine_Strider.Leg_Hind_Bone1"
"parent" "Combine_Strider.Leg_Hind_Bone"
"mass" "137.739014"
"surfaceprop" "strider"
"damping" "0.000000"
"rotdamping" "1.000000"
"inertia" "1.000000"
"volume" "109397.476563"
}
ragdollconstraint
{
"parent" "7"
"child" "8"
"xmin" "0.000000"
"xmax" "0.000000"
"xfriction" "2.000000"
"ymin" "0.000000"
"ymax" "0.000000"
"yfriction" "2.000000"
"zmin" "-32.000000"
"zmax" "138.000000"
"zfriction" "0.200000"
}
editparams
{
"rootname" "combine_strider.body_bone"
"totalmass" "2000.000000"
}
break
{
"model" "Gibs/Strider_Gib1"
"health" "0"
"fadetime" "0"
"placementbone" "Combine_strider.body_bone"
}
break
{
"ragdoll" "Gibs/Strider_Head"
"health" "0"
"fadetime" "0"
}