Bphsh: Difference between revisions
Initial page for bphsh |
Add very likely (but not fully confirmed through code yet) guess for second array with caveat. Evidence includes: layer hit mask size change between games, layer hit masks being included in PhiveConfig MaterialPreset, clamping of size to material array |
||
| Line 37: | Line 37: | ||
|} | |} | ||
=== | === Layer Hit Mask Array (Not Fully Confirmed) === | ||
In previous titles from NSO PT the layer hit masks were 32-bits each, making each element 0x8 bytes. A bphsh may have different counts for materials and layer hit masks. Phive clamps the size of the material array and layer hit mask array to the smaller of the two. HknpMeshShape shapeTags are presumably used to map Havok's geometry to Phive's material and layer hit masks. | |||
{| class="wikitable" | |||
|+ | |||
!Offset | |||
!Size | |||
!Type | |||
!Description | |||
|- | |||
|0x0 | |||
|0x8 | |||
|u64 | |||
|Layer Hit Mask.The PhiveConfig byaml stores an application's layers under LayerEntityCollection for entity rigid bodies and LayerSensorCollection for sensor rigid bodies. Each bit in the mask corresponds to a layer in the order they are defined by PhiveConfig. | |||
|- | |||
|0x8 | |||
|0x8 | |||
|u64 | |||
|Sub Layer Hit Mask.The PhiveConfig byaml stores an application's layers under SubLayerEntityCollection for entity rigid bodies and SubLayerSensorCollection for sensor rigid bodies. Each bit in the mask corresponds to a layer in the order they are defined by PhiveConfig. | |||
|} | |||
== Structures (Havok Physics) == | == Structures (Havok Physics) == | ||
| Line 122: | Line 139: | ||
|} | |} | ||
{| class="wikitable" | {| class="wikitable" | ||
|+hkReferencedObject | |+hkReferencedObject : hkBaseObject | ||
!Offset | !Offset | ||
!Size | !Size | ||
Revision as of 12:02, 20 February 2026
Bphsh is a "shape" file format. Responsible for storing the underlying physics engine's collision mesh data as well as relevant phive specific data. This page is focused on the version found in Nintendo Switch Online: Playtest Program (021), and Havok 2023.2.
File Structure
- Header
- Havok Tag File
- Material Array
- Unknown Array
Structures (phive)
Header
Common phive header for shape. The sections consist of the Havok tagfile containing an hknpMeshShape, an array of phive materials, and a currently unknown section.
Material Array
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x4 | u32 | Material Id. The PhiveConfig byaml stores an application's materials under MaterialCollection. |
| 0x4 | 0x4 | u32 | Sub Material. The PhiveConfig byaml stores an application's sub materials under SubMaterialCollection. |
| 0x8 | 0x8 | u64 | User Shape Tag Mask. Up to 64 tags can be present, each bit denotes whether a tag exists. The PhiveConfig byaml stores an application's tags under UserShapeTagCollection. |
Layer Hit Mask Array (Not Fully Confirmed)
In previous titles from NSO PT the layer hit masks were 32-bits each, making each element 0x8 bytes. A bphsh may have different counts for materials and layer hit masks. Phive clamps the size of the material array and layer hit mask array to the smaller of the two. HknpMeshShape shapeTags are presumably used to map Havok's geometry to Phive's material and layer hit masks.
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x8 | u64 | Layer Hit Mask.The PhiveConfig byaml stores an application's layers under LayerEntityCollection for entity rigid bodies and LayerSensorCollection for sensor rigid bodies. Each bit in the mask corresponds to a layer in the order they are defined by PhiveConfig. |
| 0x8 | 0x8 | u64 | Sub Layer Hit Mask.The PhiveConfig byaml stores an application's layers under SubLayerEntityCollection for entity rigid bodies and SubLayerSensorCollection for sensor rigid bodies. Each bit in the mask corresponds to a layer in the order they are defined by PhiveConfig. |
Structures (Havok Physics)
Common
hkEnum<T,B>, hkFlags<T,B>
Enum of type T, with underlying storage of type B.
hkRelPtr<T>
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x8 | hkInt64 | m_offset; Offset relative to the base address of this object to the template type T. |
hkRelArrayView<T,B>
B is an integer type.
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | sizeof(B) | B | m_offset; Offset relative to the base address of this object to the array of template type T. |
| sizeof(B) | sizeof(B) | B | m_size; Count of elements in the array |
hkRelArray<T>
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x8 | hkInt64 | m_offset; Offset relative to the base address of this object to the array of template type T. |
| 0x8 | 0x4 | int | m_size; Count of elements in the array |
| 0xc | 0x4 | int | m_capacityAndFlags; Max capacity of elements the array can store, also flags. |
hknpMeshShape
Stores a collision mesh geometry and acceleration structures.
Note; hkBaseObject has an alignment of 8, but hknpMeshShape promotes the alignment to 0x10 due to containing an hkVector4. Unmarked space is padding.
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | u64 | 0x8 | _vft_reserve; Vtable pointer, resolved at runtime. |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x8 | 0x8 | hkUlong | m_sizeAndFlags |
| 0x10 | 0x8 | hkUlong | m_refCount |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x18 | 0x1 | hkEnum<hknpShapeType::Enum, hkUint8> | m_type; Should always be "MESH" (0x8) for an hknpMeshShape. |
| 0x19 | 0x1 | hkEnum<hknpCollisionDispatchType::Enum, hkUint8> | m_dispatchType |
| 0x1a | 0x2 | hkFlags<hknpShape::FlagsEnum, hkUint16> | m_flags |
| 0x1c | 0x1 | hkUint8 | m_numShapeKeyBits |
| 0x1d | 0x3 | ||
| 0x20 | 0x4 | hkReal | m_convexRadius |
| 0x24 | 0x4 | ||
| 0x28 | 0x8 | hkUint64 | m_userData, this is set at runtime to a phive structure containing references to the material and unknown array. |
| 0x30 | 0x10 | hknpShapeProperties | m_properties |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x40 | 0x4 | hkUint32 | m_shapeTagCodecInfo |
| 0x44 | 0x4 |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x48 | 0x8 | ||
| 0x50 | 0x20 | hknpMeshShapeVertexConversionUtil | m_vertexConversionUtil |
| 0x70 | 0x8 | hkRelArrayView<hknpMeshShape::ShapeTagTableEntry, int> | m_shapeTagTable |
| 0x78 | 0x18 | hkcdSimdTreeNamespace::hkcdSimdTree<hkcdSimdTreeNamespace::RelArrayStorage<hkcdSimdTreeNamespace::Node>> | m_topLevelTree |
| 0x90 | 0x8 | hkRelArrayView<hknpMeshShape::GeometrySection, int> | m_geometrySections; Appears to be a convex decomposition, or at least partitioning, of the mesh shape. |
| 0x98 | 0x8 | hkRelPtr<hknpMeshShapePrimitiveMapping> | m_primitiveMapping |
hknpShapeType::Enum
| Name | Value | Description |
|---|---|---|
| CONVEX | 0x0 | |
| SPHERE | 0x1 | |
| CAPSULE | 0x2 | |
| CYLINDER | 0x3 | |
| TRIANGLE | 0x4 | |
| BOX | 0x5 | |
| COMPOSITE | 0x6 | |
| MESH | 0x7 | |
| EXTERN_MESH | 0x8 | |
| COMPOUND | 0x9 | |
| DISTANCE_FIELD_BASE | 0xa | |
| HEIGHT_FIELD | 0xb | |
| DISTANCE_FIELD | 0xc | |
| PARTICLE_SYSTEM | 0xd | |
| TRANSFORMED | 0xe | |
| MASKED | 0xf | |
| MASKED_COMPOUND | 0x10 | |
| BREAKABLE_COMPOUND | 0x11 | |
| LOD | 0x12 | |
| DUMMY | 0x13 | |
| LEGACY_COMPRESSED_MESH | 0x14 | |
| USER_0 | 0x15 | |
| USER_1 | 0x16 | |
| USER_2 | 0x17 | |
| USER_3 | 0x18 | |
| USER_4 | 0x19 | |
| USER_5 | 0x1b | |
| USER_6 | 0x1b | |
| USER_7 | 0x1c | |
| NUM_SHAPE_TYPES | 0x1d | |
| INVALID | 0x1e |
hknpCollisionDispatchType::Enum
| Name | Value | Description |
|---|---|---|
| NONE | 0x0 | |
| CONVEX | 0x1 | |
| COMPOSITE | 0x2 | |
| DISTANCE_FIELD | 0x3 | |
| USER_0 | 0x4 | |
| USER_1 | 0x5 | |
| USER_2 | 0x6 | |
| USER_3 | 0x7 | |
| USER_4 | 0x8 | |
| USER_5 | 0x9 | |
| USER_6 | 0xa | |
| USER_7 | 0xb | |
| NUM_TYPES | 0xc |
hknpShape::FlagsEnum
| Name | Value | Description |
|---|---|---|
| IS_CONVEX_SHAPE | 0x1 | |
| IS_CONVEX_POLYTOPE_SHAPE | 0x2 | |
| IS_COMPOSITE_SHAPE | 0x4 | |
| IS_HEIGHT_FIELD_SHAPE | 0x8 | |
| USE_SINGLE_POINT_MANIFOLD | 0x10 | |
| IS_INTERIOR_TRIANGLE | 0x20 | |
| SUPPORTS_COLLISIONS_WITH_INTERIOR_TRIANGLES | 0x40 | |
| USE_NORMAL_TO_FIND_SUPPORT_PLANE | 0x80 | |
| IS_TRIANGLE_OR_QUAD_SHAPE | 0x100 | |
| IS_QUAD_SHAPE | 0x200 | |
| IS_SDF_EDGE_COLLISION_ENABLED | 0x400 | |
| HAS_SURFACE_VELOCITY | 0x800 | |
| USER_FLAG_0 | 0x1000 | |
| USER_FLAG_1 | 0x2000 | |
| USER_FLAG_2 | 0x4000 |
hknpShapeProperties
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x10 | hkRelArray<hknpShapeProperties::Entry> | Optional shape properties. |
hknpShapeProperties::Entry
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x8 | hkRelPtr<hknpShapePropertyBase> | Pointer to shape property. If present, likely a derived class such as hknpShapeConnectivityGraph, hknpRefDragProperties, hknpShapeMassProperties, hknpShapeMassPropertiesTree, hknpShapeMassConfigProperty, or hknpMaterialPalette. |
hknpShapePropertyBase
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x2 | hkUint16 | m_propertyKey; Appears to be a derived class unique identifier. |
hknpMeshShapeVertexConversionUtil
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x10 | hkVector4 | m_bitScale16; Appears to be vertex compression parameters. |
| 0x10 | 0x10 | hkVector4 | m_bitScale16Inv |
hknpMeshShape::ShapeTagTableEntry
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x4 | hkUint32 | m_meshPrimitiveKey |
| 0x4 | 0x2 | hkUint16 | m_shapeTag |
| 0x6 | 0x2 |
hkcdSimdTreeNamespace::hkcdSimdTree<hkcdSimdTreeNamespace::RelArrayStorage<hkcdSimdTreeNamespace::Node>>
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x10 | hkRelArray<hkcdSimdTreeNamespace::Node> | m_nodes |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x10 | 0x4 | hkUint32 | m_firstFreeIndex |
| 0x14 | 0x1 | bool | m_isCompact |
| 0x15 | 0x3 |
hkcdSimdTreeNamespace::Node
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x10 | hkVector4 | m_lx |
| 0x10 | 0x10 | hkVector4 | m_hx |
| 0x20 | 0x10 | hkVector4 | m_ly |
| 0x30 | 0x10 | hkVector4 | m_hy |
| 0x40 | 0x10 | hkVector4 | m_lz |
| 0x50 | 0x10 | hkVector4 | m_hz |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x60 | 0x10 | hkUint32[4] | m_data |
| 0x70 | 0x4 | hkUint8[4] | m_filterMasks |
| 0x74 | 0x1 | hkFlags<hkcdSimdTreeNamespace::Node::FlagsEnum, unsigned char> | m_flags |
| 0x75 | 0x1 | hkUint8 | m_maxDepth |
| 0x76 | 0x1 | hkUint8 | m_splittingAxisCache |
| 0x77 | 0x1 | hkUint8 | m_numAddRemoveOperationsSinceLastRebalance |
| 0x78 | 0x4 | hkUint32 | m_parent |
| 0x7c | 0x4 | hkUint32 | m_numLeaves |
hkcdSimdTreeNamespace::Node::FlagsEnum
| Name | Value | Description |
|---|---|---|
| NODE_IS_LEAF | 0x1 | |
| NODE_IS_FREE | 0x2 | |
| INCREMENTAL_OPTIMIZER_NODES_ARE_REBALANCED | 0x4 | |
| INCREMENTAL_OPTIMIZER_REINSERT_PASS_DID_NOP | 0x8 | |
| INCREMENTAL_OPTIMIZER_LEAVES_ARE_REBALANCED | 0x10 | |
| USER_FLAG_0 | 0x20 | |
| USER_FLAG_1 | 0x40 | |
| USER_FLAG_2 | 0x80 |
hknpMeshShape::GeometrySection
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x8 | hkRelArrayView<hknpAabb8TreeNode, int> | m_sectionBvh |
| 0x8 | 0x8 | hkRelArrayView<hknpMeshShape::GeometrySection::Primitive, int> | m_primitives |
| 0x10 | 0x8 | hkRelArrayView<hkUint8, int> | m_vertexBuffer |
| 0x18 | 0x8 | hkRelArrayView<hkUint8, int> | m_interiorPrimitiveBitField |
| 0x20 | 0xc | hkUint32[3] | m_sectionOffset |
| 0x2c | 0xc | hkFloat3 | m_bitScale8Inv |
| 0x38 | 0x6 | hkInt16[3] | m_bitOffset |
| 0x3e | 0x2 |
hknpAabb8TreeNode
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x4 | hkUint32 | m_lx |
| 0x4 | 0x4 | hkUint32 | m_hx |
| 0x8 | 0x4 | hkUint32 | m_ly |
| 0xc | 0x4 | hkUint32 | m_hy |
| 0x10 | 0x4 | hkUint32 | m_lz |
| 0x14 | 0x4 | hkUint32 | m_hz |
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x18 | 0x3 | hkUint8[3] | m_data |
| 0x1b | 0x1 |
hknpMeshShape::GeometrySection::Primitive
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x1 | hkUint8 | m_aId |
| 0x1 | 0x1 | hkUint8 | m_bId |
| 0x2 | 0x1 | hkUint8 | m_cId |
| 0x3 | 0x1 | hkUint8 | m_dId |
hknpMeshShape::GeometrySection::Vertex16_3
While m_vertexBuffer is untyped in later versions, in previous version it assumed this vertex format struct, which still appears to be the case (u8 mainly forces the m_vertexBuffer.m_size to be in bytes)
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x2 | hkUint16 | m_x |
| 0x2 | 0x2 | hkUint16 | m_y |
| 0x4 | 0x2 | hkUint16 | m_z |
hknpMeshShapePrimitiveMapping
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x0 | 0x10 | hkRelArray<hkUint32> | m_sectionStart |
| 0x10 | 0x10 | hkRelArray<unsigned int> | m_bitString |
| 0x20 | 0x4 | hkUint32 | m_bitsPerEntry |
| 0x24 | 0x4 | hkUint32 | m_triangleIndexBitMask |