The goal of cast is to create an easy to use format for models, animations, materials, and game worlds. In addition, cast should be able to produce the same scenes in any 3d software.
- .NET Framework (Reference): Libraries/DotNet
- .NET Framework (by Scobalula): Cast.NET
- Python: Libraries/Python
- CastModelViewer (By echo000): Github
- SECast, a lossless converter to cast: SECast
- Note: If your tool supports exporting to cast directly, that is always better.
- Frequently asked questions: FAQ
All files start with a cast header:
struct CastHeader
{
uint32_t Magic; // char[4] cast (0x74736163)
uint32_t Version; // 0x1
uint32_t RootNodes; // Number of root nodes, which contain various sub nodes if necessary
uint32_t Flags; // Reserved for flags, or padding, whichever is needed
};
A cast file is basically a group of generic nodes. Nodes are given a unique registered id, which can tell the loader what the data is, and how to handle it.
Following the cast header is a collection of nodes which must be of type CastId::Root.
A node looks like:
struct CastNodeHeader
{
CastId Identifier; // Used to signify which class this node uses
uint32_t NodeSize; // Size of all data and sub data following the node
uint64_t NodeHash; // Unique hash, like an id, used to link nodes together
uint32_t PropertyCount; // The count of properties
uint32_t ChildCount; // The count of direct children nodes
// We must read until the node size hits, and that means we are done.
// The nodes are in a stack layout, so it's easy to load, FILO order.
};
There are several registered cast ids available:
enum class CastId : uint32_t
{
Root = 0x746F6F72,
Model = 0x6C646F6D,
Mesh = 0x6873656D,
BlendShape = 0x68736C62,
Skeleton = 0x6C656B73,
Bone = 0x656E6F62,
IKHandle = 0x64686B69,
Constraint = 0x74736E63,
Animation = 0x6D696E61,
Curve = 0x76727563,
CurveModeOverride = 0x564F4D43,
NotificationTrack = 0x6669746E,
Material = 0x6C74616D,
File = 0x656C6966,
Instance = 0x74736E69,
Metadata = 0x6174656D,
};
Following a node, is the list of properties [Node.PropertyCount], a property looks like:
struct CastPropertyHeader
{
CastPropertyId Identifier; // The element type of this property
uint16_t NameSize; // The size of the name of this property
uint32_t ArrayLength; // The number of elements this property contains (1 for single)
// Following is UTF-8 string lowercase, size of namesize, NOT null terminated
// cast_property[ArrayLength] array of data
};
For properties, cast has several built in types:
enum class CastPropertyId : uint16_t
{
Byte = 'b', // <uint8_t>
Short = 'h', // <uint16_t>
Integer32 = 'i', // <uint32_t>
Integer64 = 'l', // <uint64_t>
Float = 'f', // <float>
Double = 'd', // <double>
String = 's', // Null terminated UTF-8 string
Vector2 = 'v2', // Float precision vector XY
Vector3 = 'v3', // Float precision vector XYZ
Vector4 = 'v4' // Float precision vector XYZW
};
To read a cast file, you just need to traverse the root nodes and their children. Properties always come before a nodes children. Each node has the total size of itself, and all children, so if a processor doesn't understand a node id, it can skip the entire node and continue reading.
Cast ids are stored as integers to make it faster to serialize and deserialize.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | Skeleton, Mesh, Material | True | False |
Parent | Root | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | False |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Model | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | False |
Vertex Position Buffer (vp) | Vector 3 (v3) | True | True |
Vertex Normal Buffer (vn) | Vector 3 (v3) | True | False |
Vertex Tangent Buffer (vt) | Vector 3 (v3) | True | False |
Vertex Color Buffer (c%d) | Integer 32 (i) | True | False |
Vertex UV Buffer (u%d) | Vector 2 (v2) | True | False |
Vertex Weight Bone Buffer (wb) | Integer 32 (i), Short (h), Byte (b) | True | False |
Vertex Weight Value Buffer (wv) | Float (f) | True | False |
Face Buffer (f) | Integer 32 (i), Short (h), Byte (b) | True | True |
Color Layer Count (cl) | Integer 32 (i), Short (h), Byte (b) | False | True if has color layers else False |
UV Layer Count (ul) | Integer 32 (i), Short (h), Byte (b) | False | True if has uv layers else False |
Maximum Weight Influence (mi) | Integer 32 (i), Short (h), Byte (b) | False | True if has weights else False |
Skinning Method (sm) | String (s) [linear, quaternion] | False | False |
Material (Hash of CastNode:Material) (m) | Integer 64 (l) | False | False |
Notes:
Face Buffer
is an index into the current meshes vertex data buffers where (0, 1, 2) are the first three vertices from this mesh.- The
Face Buffer
follows CCW (right-handed) winding order, this may be different in other apis, where you may have to remap the indices. - If a face contains an invalid index combination
(0, 1, 1), (0, 1, 0), (0, 0, 0)
where two or more indices are the same, it is acceptable for the user processing these faces to ignore them in order to properly render the mesh. It would be wise to present the user with a warning stating that this happened. - Each vertex descriptor buffer must contain the same number of elements ex: if you have 16 vertices, you must have 16 normals if they exist, 16 colors if the buffer exists. Otherwise it's assumed they are default / skipped.
- Weights are additive which means having the same bone with
0.5
and0.5
would end up making that bones influence1.0
for example. - The default skinning method is
linear
. When set toquaternion
dual quaternion skinning is used. - NEW 8/18/2024: The vertex color specification has changed, in order to support multiple color layers, a new
Color Layer Count (cl)
was added which mimics theUV Layer Count (ul)
property.- To be backwards compatible, cast processors should check for
cl
, and use that by default along with the newc%d
layer properties. - If the
cl
property does not exist, a processor should check for the legacyvc
property which is the one and only color layer if it exists.
- To be backwards compatible, cast processors should check for
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Model | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | True |
Base Shape (Hash of CastNode:Mesh) (b) | Integer 64 (l) | False | True |
Target Shape Vertex Indices (vi) | Byte (b), Short (h), Integer 32 (i) | True | True |
Target Shape Vertex Positions (vp) | Vector 3 (v3) | True | True |
Target Weight Scale (ts) | Float (f) | True | False |
Notes:
- The
Base Shape
must be an existing cast mesh. - The
Target Shape Vertex Indices
andTarget Shape Vertex Positions
must be the same length as they are paired together. Target Shape Vertex Positions
is the final value of each changed vertex position ignoring theBase Shape
's corresponding vertex.Target Weight Scale
indicates the maximum value the target shape can deform to and should default to1.0
.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | Bone, IKHandle, Constraint | True | False |
Parent | Model | False | True |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Skeleton | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | True |
Parent Index (p) | Integer 32 (i) | False | False |
Segment Scale Compensate (ssc) | Byte (b) [True, False] | False | False |
Local Position (lp) | Vector 3 (v3) | False | False |
Local Rotation (lr) | Vector 4 (v4) | False | False |
World Position (wp) | Vector 3 (v3) | False | False |
World Rotation (wr) | Vector 4 (v4) | False | False |
Scale (s) | Vector 3 (v3) | False | False |
Notes:
Segment Scale Compensate
should default toTrue
when not specified.Scale
is always local to the current bone.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Skeleton | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | False |
Start Bone Hash (sb) | Integer 64 (l) | False | True |
End Bone Hash (eb) | Integer 64 (l) | False | True |
Target Bone Hash (tb) | Integer 64 (l) | False | False |
Pole Vector Bone Hash (pv) | Integer 64 (l) | False | False |
Pole Bone Hash (pb) | Integer 64 (l) | False | False |
Use Target Rotation (tr) | Byte (b) [True, False] | False | False |
Notes:
Use Target Rotation
should default toFalse
when not specified.Pole Bone
must only effect the twist of the chain, in general you either have aPole Bone
or aPole Vector Bone
.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Skeleton | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | False |
Constraint Type (ct) | String (s) [pt, or, sc] | False | True |
Constraint Bone Hash (cb) | Integer 64 (l) | False | True |
Target Bone Hash (tb) | Integer 64 (l) | False | True |
Maintain Offset (mo) | Byte (b) [True, False] | False | False |
Skip X (sx) | Byte (b) [True, False] | False | False |
Skip Y (sy) | Byte (b) [True, False] | False | False |
Skip Z (sz) | Byte (b) [True, False] | False | False |
Notes:
- The constraint type values correspond to:
pt
Point Constraint, which applies to translations.or
Orient Constraint, which applies to rotations.sc
Scale Constraint, which applies to scales.
- Maintain offset should default to
False
when not specified. - Skip X, Skip Y, and Skip Z should default to
False
when not specified and refer to ignoring that axis in the constraint.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | File | True | False |
Parent | Model | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | True |
Type (t) | String (s) | False | True |
Albedo File Hash (albedo) | Integer 64 (l) | False | False |
Diffuse File Hash (diffuse) | Integer 64 (l) | False | False |
Normal File Hash (normal) | Integer 64 (l) | False | False |
Specular File Hash (specular) | Integer 64 (l) | False | False |
Emissive File Hash (emissive) | Integer 64 (l) | False | False |
Gloss File Hash (gloss) | Integer 64 (l) | False | False |
Roughness File Hash (roughness) | Integer 64 (l) | False | False |
Ambient Occlusion File Hash (ao) | Integer 64 (l) | False | False |
Cavity File Hash (cavity) | Integer 64 (l) | False | False |
Anisotropy File Hash (aniso) | Integer 64 (l) | False | False |
Extra (x) File Hash (extra%d) | Integer 64 (l) | False | False |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | CastNode | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Path (p) | String (s) | False | True |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | Skeleton, Curve, CurveModeOverride, NotificiationTrack | True | True |
Parent | Root | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | False |
Framerate (fr) | Float (f) | False | True |
Looping (lo) | Byte (b) [True, False] | False | False |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Animation | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Node Name (nn) | String (s) | False | True |
Key Property Name (kp) | String (s) [rq, tx, ty, tz, sx, sy, sz, bs, vb] | False | True |
Key Frame Buffer (kb) | Byte (b), Short (h), Integer 32 (i) | True | True |
Key Value Buffer (kv) | Byte (b), Short (h), Integer 32 (i), Float (f), Vector 4 (v4) | True | True |
Mode (m) | String (s) [additive, absolute, relative] | False | True |
Additive Blend Weight (ab) | Float (f) | False | False |
Notes:
- All curve keyframes are in object/node space.
- The
Mode
determines how each curve keyframe is applied to the node.additive
: The keyframe is added to the current scene frame value of the nodes property.absolute
: The keyframe is the exact value for the given frame.relative
: The keyframe is added to the rest position value of the nodes property.
- The property values correspond to:
rq
Rotation Quaternion and expectsv4
values.tx
Translation 'X' and expectsf
values.ty
Translation 'Y' and expectsf
values.tz
Translation 'Z' and expectsf
values.sx
Scale 'X' and expectsf
values.sy
Scale 'Y' and expectsf
values.sz
Scale 'Z' and expectsf
values.bs
BlendShape Weight and expectsf
values.vb
Visibility and expectsb
,h
, ori
values.=0
= hidden.>=1
= visible.
- The properties
tx
,ty
,tz
,sx
,sy
,sz
,bs
,vb
should interpolate linearly. - The property
rq
should interpolate with quaternion slerp.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Animation | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Node Name (nn) | String (s) | False | True |
Mode (m) | String (s) [additive, absolute, relative] | False | True |
Override Translation Curves (ot) | Byte (b) [True, False] | False | False |
Override Rotation Curves (or) | Byte (b) [True, False] | False | False |
Override Scale Curves (os) | Byte (b) [True, False] | False | False |
Notes:
- See
Curve
notes above for the definition of eachMode
value. Override Translation Curves
should default toFalse
when not specified.Override Rotation Curves
should default toFalse
when not specified.Override Scale Curves
should default toFalse
when not specified.- The override node and all of it's children should override their curves mode to the new mode.
- The override node must be present at the time of processing in order to determine if a child bone is a descendent.
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Animation | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | True |
Key Frame Buffer (kb) | Byte (b), Short (h), Integer 32 (i) | True | True |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | File | True | False |
Parent | Root | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Name (n) | String (s) | False | False |
Reference File (Hash of CastNode:File) (rf) | Integer 64 (l) | False | True |
Position (p) | Vector 3 (v3) | False | True |
Rotation (r) | Vector 4 (v4) | False | True |
Scale (s) | Vector 3 (v3) | False | True |
Field | Type(s) | IsArray | Required |
---|---|---|---|
Children | None | True | False |
Parent | Root | False | True |
Property (id) | Type(s) | IsArray | Required |
---|---|---|---|
Author (a) | String (s) | False | False |
Software (s) | String (s) | False | False |
Up Axis (up) | String (s) [x, y, z] | False | False |
Notes:
Author
andSoftware
are just for tagging cast files and have no use outside of metadata.Up Axis
can be used as a hint to software to adjust the scene to match a specific up axis.- A cast file can have any number of meta nodes but properties designed for hinting should only use the first metadata node instance.
- Format designed by DTZxPorter with input from the community.
- Icons by Smashicons