OpenKh

This is a centralized place for the documentation and other discoveries about the internal working of Kingdom Hearts games.

View on GitHub

Kingdom Hearts: Birth By Sleep - Models

Found inside PMP files or as raw PMO files, BBS stores it’s models in PMO blobs.

The format supports varying vertex types and has support for animation bones and two pass rendering.

PMO Header

Offset Type Description
0x0 uint32 Magic Value. Always “PMO\0” (0x004F4D50)
0x4 uint8 Number
0x5 uint8 Group
0x6 uint8 Version
0x7 uint8 Padding
0x8 uint8 Texture Count
0x9 uint8 Padding
0xA uint16 Flag
0xC uint32 Skeleton Offset
0x10 uint32 Mesh Offset 0
0x14 uint16 Triangle Count
0x16 uint16 Vertex Count
0x18 float Model Scale
0x1C uint32 Mesh Offset 1
0x20 float[4][8] Bounding Box

Texture Info

The Header is followed by Texture Count Texture Info records

Offset Type Description
0x0 uint32 Offset of TM2 block if present in current file.
0x4 char[12] Texture Name
0x10 float Tiling Speed X
0x14 float Tiling Speed Y
0x18 int32[2] Padding

See also: TIM2

Mesh Sections

Mesh Offset 0 and Mesh Offset 1 both point to the start of a list of Mesh Section Headers. You keep reading headers + their verts until you encounter a 0 Vertex Count. This structure must be aligned on a 4 byte boundary.

Offset Type Description
0x0 uint16 Vertex Count
0x2 int8 Texture ID, an index into the list of Texture Info records
0x3 uint8 Vertex Size, in bytes
0x4 uint32 Vertex Flags, see below
0x8 uint8 Group
0x9 uint8 Triangle Strip count
0xA uint16 Vertex Attribute
0xC uint8[8] List of bone indices that affect this section. Only present if the PMO blob contains a skeleton (ie: header.skeletonOffset != 0)
varies uint32 Diffuse Color. Only present if vertexFlags.DiffuseColor & 1

The section header is followed by vertexCount * vertexSize bytes of vertex data. The next Section header follows on the next 4-byte alignment after that.

Vertex flags

The 24 least significant bits of this structure correspond to the PSP’s GE VTYPE command. The Primitive type bits correspond to the primitive type bits of the PSP’s GE PRIM command.

Bit Count Description
0 2 Texture Coordinate Format
2 3 Color Format
5 2 Normal Format (Not used by BBS)
7 2 Position Format
9 2 Weight Format
11 2 Indices Format (Not used by BBS)
13 1 Unused
14 3 Skinning Weights Count
17 1 Unused
18 3 Number of morphing weights (Not used by BBS)
21 2 Unused
23 1 Skip Transform Pipline (Not used by BBS)
24 1 Uniform Diffuse Flag
25 3 Unknown, possibly unused
28 4 Primitive Type

Coordinate Format

Texture Format, Normal Format, Position Format and Weight Format are as follows:

Value Meaning
0x0 Not Present In Vertex
0x1 8-bit normalized
0x2 16-bit normalized
0x3 32-bit float

Weight format has only been observed to use 8-bit fixed in actual PMO blobs.

Color Format

Color Format is as follows:

Value Meaning
0x0 Not Present In Vertex
0x4 16-bit BGR-5650
0x5 16-bit ABGR-5551
0x6 16-bit ABGR-4444
0x7 32-bit ABGR-8888

Only 32-bit ABGR-8888 has been observed in use by actual PMO blobs. If the Uniform Diffuse Flag is set, this value should be 0.

The number of skinning/morphing weights to be read is actually the value in the bit field + 1. If the weight format field is 0, these fields should be ignored.

Index Format has not been seen in actual PMO blobs, but if it is used the expected format is as follows:

Value Meaning
0x0 Not using indices
0x1 8-bit
0x2 16-bit

Uniform Diffuse Flag indicates that all vertices in the section should use the same vertex color, which follows the header before the vertex data. See header structure above.

Primitive Type is as follows:

Value Meaning
0x0 Points
0x1 Lines
0x2 Line Strips
0x3 Triangles
0x4 Triangle Strips
0x5 Triangle Fans
0x6 Sprites (Quads)

Only Triangles and Triangle Strips have been observed in actual PMO blobs.

Decoding Verts

Once the header has been read, along with any optional fields present, the vertex data immediatly follows. Each vertex follows the format described by the vertexFlags field. The vertex properties are written in the following order. Note that only position is mandatory.

Name
Joint Weights
Texture Coords
Color
Position

Reading ‘Normalized’ values

Vertex properties which are in the format 8-bit normalized or 16-bit normalized need to be converted to floating point values before rendering. Note that position values should read as signed values, the others as unsigned values. The conversion is as follows:

	// uint8
	float value = (float)data / 127.0f;
	// uint16
	float value = (float)data / 32767.0f;

Vertex Attribute

This field defines what kind of primitive is being rendered.

Value Name Meaning
0 ATTR_BLEND_NONE  
1 ATTR_NOMATERIAL  
2 ATTR_GLARE  
4 ATTR_BACK  
8 ATTR_DIVIDE  
16 ATTR_TEXALPHA Specifies that the texture used contains alpha values for transparency
24 FLAG_SHIFT  
28 PRIM_SHIFT  
32 ATTR_BLEND_SEMITRANS Specifies that the material must be blended as semitransparent
64 ATTR_BLEND_ADD  
96 ATTR_BLEND_SUB  
224 ATTR_BLEND_MASK  
256 ATTR_8  
512 ATTR_9  
1024 ATTR_DROPSHADOW  
2048 ATTR_ENVMAP  
4096 ATTR_12  
8192 ATTR_13  
16384 ATTR_14  
32768 ATTR_15  
16777216 FLAG_COLOR  
33554432 FLAG_NOWEIGHT  

Skeleton Header

Offset Type Description
0x0 uint32 Magic Value. Always “BON\0”
0x4 uint32 padding
0x8 uint16 Bone Count
0xA uint16 padding
0xC uint16 Skinned Bones
0xE uint16 Skinned Bones Initial Index

Joint Definition

Offset Type Description
0x0 uint16 Bone Index
0x2 uint16 Padding
0x4 uint16 Parent Bone Index - 0xFFFF indicates no parent
0x6 uint16 Padding
0x8 uint16 Skinning Index
0xA uint16 Padding
0xC uint32 Padding
0x10 char[16] Bone Name
0x20 Matrix4x4 Transform
0x60 Matrix4x4 Inverse Transform