DMX model

From Valve Developer Community
Jump to: navigation, search

The DMX model format replaces Studiomdl Data. This article describes version 18.

Note:All names are case sensitive.


These features require DMX:


Source 2007
Source 2009
Half-Life 2
Source SDK Base Singleplayer 2013
Source SDK Base Multiplayer 2013
Left 4 Dead
Left 4 Dead 2
Alien Swarm
Source MP
Portal 2
Source Filmmaker



// A "DAG" is a generic Maya container, appearing here in order to transform objects.
// It can take the place of a DmeMesh, DmeJoint or DmeAttachment at any time.
class DmeDag
	DmeTransform	transform;	// used by SFM to store the *current* position of the dag.
								// Studiomdl only reads this value if nothing is found in DmeModel::baseStates::DmeTransformList.
	bool			visible;
	void			children[]; // never seen used (Compatibility with DmeModel?)
	// One of the following:
	CUtlString		name; // of a DmeJoint
	DmeMesh		shape;
	DmeAttachment	shape;
class DmeJoint // a bone
	DmeTransform	transform;	// used by SFM to store the *current* position of the bone.
								// Studiomdl only reads this value if nothing is found in DmeModel::baseStates::DmeTransformList.
	void			shape; // only seen empty
	bool			visible;
	DmeJoint		children[];
	bool			lockInfluenceWeights;
class DmeTransformList
	DmeTransform	transforms[]; // contains one element for each bone and mesh. Elements are matched according to name.
class DmeTransform
	Vector		position;
	Quaternion	orientation;


class DmeModelRoot
	// model and skeleton should point to the same object
	DmeModel	model;
	DmeModel	skeleton;
	DmeCombinationOperator	combinationOperator; // flex controllers; optional
class DmeModel // one per file
	bool				visible;
	DmeDag|DmeJoint		children[];		// mixed type, contains DmeMesh elements too
	DmeJoint			jointList[];
	DmeTransformList	baseStates[];	// defines bone and mesh positions; only ever seen with one value	
class DmeAttachment // an attachment
	bool		visible;
	bool		isRigid; // apparently obsolete?
	bool		isWorldAligned; // does not move with parent bone, but is still positioned relative to it
class DmeMesh
	bool				visible;
	void				bindState;		// only seen empty
	DmeVertexData		currentState;	// pointer to default baseState		
	DmeVertexData		baseStates[];	// only ever seen with one value	
	DmeVertexDeltaData	deltaStates[];	// flex shapes	
	DmeFaceSet			faceSets[];
	Vector2D			deltaStateWeights[];		// unknown
	Vector2D			deltaStateWeightsLagged[];	// unknown
class DmeVertexData // mesh data
	CUtlString	vertexFormat[];		// { positions, normals, textureCoordinates, [jointWeights, jointIndices, balance] }
	int			jointCount;			// most bones any one vert is weighted to; max 3
	bool		flipVCoordinates;	// left-handed to right-handed?
	// The first array contains one entry per vertex.
	// The second "Indices" array contains one entry one entry per vertex /per face/.
	Vector		positions[];
	int			positionsIndices[];
	Vector		normals[];
	int			normalsIndices[];
	Vector2D	textureCoordinates[];
	int			textureCoordinatesIndices[];
	// Flex controller stereo split; optional
	float		balance[];			// 0 = 100% right, 1 = 100% left.
	int			balanceIndices[];
	// Weightmapping; optional. The size of BOTH arrays is equal to ( sizeof(positions) * jointCount )
	float		jointWeights[];		// weight
	int			jointIndices[];	// index in DmeModel::jointList (v15+) or DmeModel::jointTransforms (v1-14)
class DmeFaceSet // defines a set of faces with a given material
	DmeMaterial	material; // the material these faces are drawn with
	int			faces[]; // the indices of the vertices that make up each face, delimited by -1. Quads and *convex* n-gons allowed.
class DmeMaterial // a material
	CUtlString	mtlName; // path relative to \game\materials\, no extension
class DmeVertexDeltaData // a shape key
	CUtlString	vertexFormat[]; // positions, normals, [wrinkle]
	bool		flipVCoordinates; // unknown
	bool		corrected; // unknown
	Vector3		positions[]; // offset: (shape position) - (mesh position)
	int			positionsIndices[]; // index in DmeMesh::currentState::positions
	Vector3		normals[]; // offset: (shape normal) - (mesh normal). For corrective shapes "base" is the mesh plus target shapes.
	int			normalsIndices[]; // index in DmeMesh::currentState::normals
	float		wrinkle[]; // wrinkle scale. +1 means full compress, -1 means full stretch.
	int			wrinkleIndices[]; // index in DmeMesh::currentState::textureCoordinates
  • For corrective shapes, values for DmeVertexDeltaData::normals should be calculated with all target shapes applied to the mesh.
  • It's wrinkleIndices, not wrinklesIndices!

Flex controllers

class DmeCombinationOperator // flex controller global settings
	DmeCombinationInputControl		controls[];
	Vector							controlValues[];		// rest position...but why a 3D vector?
	Vector							controlValuesLagged[];	// unknown
	bool							usesLaggedValues;
	DmeCombinationDominationRule	dominators[];
	DmeMesh							targets[]; // mesh with shapes on, or DmeFlexRules in some old DMX files
class DmeCombinationInputControl // a flex controller
	CUtlString	rawControlNames[];	// which controls are being wrapped
	bool		stereo;				// equivalent to QC 'split'
	bool		eyelid;				// flags as an eyelid used by AI for blinking
	float		wrinkleScales[];	// records the scale used to generate wrinkle data; not read by studiomdl
	float		flexMin;
	float		flexMax;
class DmeCombinationDominationRule // Disables certain shapes (NOT controllers) when others are active
	CUtlString	dominators[];
	CUtlString	supressed[];

The following element types still exist, but appear to have been obsoleted by domination rules:

class DmeFlexRules // shape key pre-processing. Insert at DmeCombinationOperator::targets.
	DmeFlexRule|DmeFlexRulePassThrough	deltaStates[]; // mixed type
	Vector2								deltaStateWeights[];
	DmeMesh								target; // mesh with the shapes on
// In the next two cases, the element name must match
// the name of a DmeVertexDeltaData element on the target DmeMesh
class DmeFlexRule
	float		result;
	CUtlString	expression; // +-/() with min, max & sqrt.
class DmeFlexRulePassThrough // no expression required
	float		result;


class DmeModelRoot
	DmeModel	skeleton;
	DmeAnimationList	animationList;
class DmeAnimationList
	DmeChannelsClip animations[];
class DmeChannelsClip
	DmeTimeFrame timeFrame;
	Colour color;		// SFM only
	CUtlString text;	// SFM only
	bool mute;			// SFM only
	int frameRate;		// typically 30
	void trackGroups[];		// SFM only
	DmeChannel channels[];	// two for each bone: position and rotation
class DmeTimeFrame
	DmeTime_t start;	// no apparent effect, use offset
	DmeTime_t duration;	// length in seconds...framerate is NOT adjusted
	DmeTime_t offset;	// remove this many seconds from the start (can be negative)
	float scale; // frameRate multiplier
class DmeChannel
	// this format is shared with Source Filmmaker, so has support for animating generic properties.
	// Studiomdl only cares about bones though.
	CDmxElement	fromElement;
	CUtlString		fromAttribute;
	int				fromIndex;
	CDmxElement	toElement; // ordinarily a DmeTransform used by the target bone
	CUtlString		toAttribute;
	int				toIndex;
	int		mode; // unknown
	// One of:
	DmeQuaternionLog	log[];
	DmeVector3Log		log[];
	// etc
class DmeQuaternionLog // also DmeVector3Log etc.
	DmeQuaternionLogLayer layers[];
	CDmxElement	curveinfo;
	bool			usedefaultvalue;
	Quaternion		defaultvalue;
class DmeQuaternionLogLayer // also DmeVector3LogLayer etc.
	// only frames where the bone moves need to be given
	// unlike SMD, sparse keyframes are supported
	DmeTime_t	times[];
	int			curvetypes[]; // only seen empty...keyframe interp?
	Quaternion	values[];


This list will inevitably be incomplete. Only versions known about by the public are listed. Format changes generally relate to Source Filmmaker, not Studiomdl.


Introduction of DmeTime attribute type.
Renamed "durationTime" to "duration"
Renamed "offsetTime" to "offset"


Added "jointList" alongside "jointTransforms"