DMX model

From Valve Developer Community
Revision as of 05:42, 26 January 2019 by Doublethinking (talk | contribs) (Cleared up some specifics which can be viewed when using the Element Viewer in SFM. Obviously unrelated to studiomdl in most cases, but still a more complete documentation of the format.)
Jump to: navigation, search

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

Note.png 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 node in a Directed acyclic graph, constructing the scene hierarchy.
// It appears here in order to transform its child 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;
	DmeDag			children[]; // child nodes of the dag. never seen used (Compatibility with DmeModel?)
	// One of the following:
	CUtlString		name; // of a DmeJoint
	DmeMesh		shape;
	DmeAttachment	shape;

class DmeJoint : DmeDag // 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				children[];
	DmeJoint			jointList[];
	DmeTransformList	baseStates[];	// defines bone and mesh positions; only ever seen with one value	

class DmeAttachment : DmeDag // 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 : DmeDag
	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 (studiomdl errors on compile otherwise)
	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 for L/R values
	Vector							controlValuesLagged[];	// lerp factor for changes to the values (if usesLaggedValues == true)
	bool							usesLaggedValues;       // value changes are not instant, but "lagged" (lerp between values - enabled by default in SMD)
	DmeCombinationDominationRule	dominators[]; // list of domination rules to use
	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[];

class DmeFlexRules // shape key pre-processing. Insert at DmeCombinationOperator::targets.
	DmeFlexRule	deltaStates[]; // mixed type
	Vector2								deltaStateWeights[];
	DmeMesh								target; // mesh with the shapes on

// In flex rules, the element name must match the name of a DmeVertexDeltaData element on the target DmeMesh.
// It does NOT specify values of controllers.
class DmeFlexRule
	float		result;
class DmeFlexRuleExpression : DmeFlexRule // Seems to be replaced with "DmeFlexRule" in older versions of DMX?
	float		result;
	CUtlString	expression; // +-/() with min, max & sqrt. Flex controller names can be included too, as long as their names don't have spaces!
                            // L/R split controllers either have the left_ or right_ prefix, or a L or R suffix.
class DmeFlexRulePassThrough : DmeFlexRule // No expression required, shapes are controlled like normal
	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

	DmeTrackGroup 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; // TODO: what should this be?
	CUtlString		fromAttribute;
	int				fromIndex;
	CDmxElement	toElement; // ordinarily a DmeTransform used by the target bone
	CUtlString		toAttribute;
	int				toIndex;
	int		mode; // Recording mode for channel - unused by studiomdl.

	// 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[]; // keyframe interp in SFM
	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"