DMX: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
m (Setting bug notice hidetested=1 param on page where the bug might not need tested in param specified)
 
(41 intermediate revisions by 14 users not shown)
Line 1: Line 1:
{{toc-right}}
{{toc-right}}


'''DMX''' (Data Model eXtensible?) is a [[W:Binary file|binary]] encoding of a [[Datamodel]] object. It is the sister format of [[KeyValues2]], which stores the same data in ASCII. The unreleased SDK tool '''dmxconvert''' can convert between the two formats.
'''DMX''' (Data Model eXchange) stores a Datamodel object, encoded in either [[W:Binary file|binary]] or ASCII. The internal structure or "format" of a DMX is defined by the user.
{{note | The SDK tool [[dmxconvert]] can convert a DMX between the two encodings.}}
==Header==


Compared to KeyValues2, DMX is:
All DMX files start with this header:
 
<pre>
<!-- dmx encoding [encoding name] [encoding version] format [format name] [format version] -->
</pre>
 
==Encodings==
===KeyValues2===
This ASCII encoding is the successor to [[KeyValues]]. It's good if you have a small DMX and want it hand-editable.
 
{{tip|1=When hand-editing you can generate IDs for new elements [http://www.guidgen.com/ with this website] (or with guidgen itself, if you have the Windows SDK installed).}}
 
===Binary===
Compared to KeyValues2, binary DMX is:


# ''Small'', especially when storing large [[int]]s or accurate [[float]]s
# ''Small'', especially when storing large [[int]]s or accurate [[float]]s
Line 10: Line 25:
# ''Inscrutable'', because it contains native binary data
# ''Inscrutable'', because it contains native binary data


DMX encoding is currently used for [[particle]] systems (<code>.pcf</code>) and [[model]] source data (<code>.dmx</code>).
Binary is good for large DMXes.


== Using DMX ==
See [[DMX/Binary]] for a technical description of the encoding.


With DMX being a binary format, it is not practical either to infer how a given file works from reading it, or to alter one by hand. When the tools for reading/writing them aren't publicly available, this is a problem!
==Formats==
These are the formats used by Valve.


=== Models ===
===Particle System===
Created by the [[Particle Editor]] and read by the engine, usually with the extension <code>pcf</code>. There should be no reason to manipulate these yourself.


DMX model source files are generated by '''<code>vsdmxio</code>''', which only available publicly as a Maya plugin (<code>sourcesdk/maya/2009/plug-ins/valveSource/</code>). Some editing options are available with the Source 2009 SDK tool '''<code>dmxedit</code>''' however, including export to OBJ (unweighted, no animations).
===Model===
The [[DMX model]] deprecates [[SMD]] in {{src07}} and newer. It can be exported by Valve's official Maya plugin (<code>sourcesdk/maya/2009/plug-ins/valveSource/</code>), the [[Blender Source Tools]] and from the 3ds Max pipeline tools in [[Wall Worm Model Tools]].


To use dmxedit:
There are some DMX-related tools in the SDK:


* Create an empty file called <code>vs.lua</code> in the SDK binaries folder. This is probably meant to contain commands that are executed every time you run the tool.
* [[dmxedit]]
* You must then create a LUA file containing the actions you want to perform. Here is a simple example that exports to OBJ:
* [[dmxconvert]]
<source lang=lua>
Load( "<path_to_dmx>" )
Save("<export_path>.obj")
</source>
* Perform <code>dmxedit myfile.lua</code> to run the script.


If export a mesh DMX to OBJ you will get a single file. If you export a morph DMX you will get one OBJ for each flex animation that was in the file. If you chose an animation you will get nothing; it seems that dmxedit doesn't support animations.
===Engine Tools===
DMX is widely used to represent data in [[engine tools]]. For example, [[Source Filmmaker]] save files are stored as DMX, and materials are converted to DMX by [[Material Editor]] in order to be edited.


== Programming ==
==Programming==
Handling DMX files has many similarities to handling [[KeyValues]]. The big architectural differences are that:


Handling DMX files has many similarities to handling [[KeyValues]]. The two big architectural differences are that:
* Where KVs had keys, Datamodel has '''<code>CDmxElement</code>''' objects containing '''<code>CDmxAttribute</code>''' (which can point to further elements).
* There is '''support for [[array]]s'''.
* Attribute values are accessed through '''a single [[W:Template (programming)|templated]] function''', not lots of different ones.
* Each element can have '''multiple parents'''. Each element has a '''128-bit ID''' to support this.


* <code>CDmxElement</code> objects contain either <code>CDmxAttribute</code> objects, which hold actual data, or more <code>CDmxElement</code>s. Arrays are possible in both cases.
===Source API===
* Values are accessed through a single [[W:Template (programming)|templated]] function, not lots of different ones.
Source has two DMX APIs: '''<code>CDmxElement</code>''' and '''<code>CDmElement</code>'''. The former is a lightweight implementation designed for use in game code; the latter is designed around the needs of the [[engine tools]] and includes heavyweight features like undo levels and element factories. The mod SDK only supports <code>CDmxElement</code>.


For a full list of attributes, see the bottom of <code>public\datamodel\dmattributetypes.h</code>.
For a full list of attribute types, see the bottom of <code>public\datamodel\dmattributetypes.h</code>.


{{note|[[String]]s are <code>[[CUtlString]]</code> objects, not <code>[[char]]</code>!}}
{{note|[[String]]s are <code>[[CUtlString]]</code> objects, not <code>[[char]]</code>!}}


{{note|The SerializeDMX() overload that includes a file path do not work. You must seralize to a buffer then write out manually.}}
====Creating====
{{bug|hidetested=1|The <code>SerializeDMX()</code> overload that requires a file path does not work. You must serialize to a buffer then write out manually.}}


=== Creating ===
<source lang=cpp highlight=17>
 
<source lang=cpp>
#include "cbase.h"
#include "cbase.h"
#include "dmxloader/dmxloader.h"
#include "dmxloader/dmxloader.h"
Line 55: Line 72:
{
{
DECLARE_DMX_CONTEXT();
DECLARE_DMX_CONTEXT();
CDmxElement* DMX = CreateDmxElement("");
CDmxElement* DMX = CreateDmxElement("MyElementTypeName");
 
DMX->AddAttribute("C++ templates")->SetValue<bool>(true);
 
DMX->AddAttribute("Hello")->SetValue("world"); // non-templated shortcut for strings


CDmxAttribute* curAttr = DMX->AddAttribute("Hello");
DMX->AddAttribute("An_Array")->GetArrayForEdit<float>().AddToTail(3.141);
curAttr->SetValue("world");


CUtlBuffer buf;
CUtlBuffer buf;
Line 69: Line 89:
</source>
</source>


=== Iterator ===
====Iterator====
 
This code will print string, int, float and bool values in the given DMX to the console. {{todo|Elegant solution to arrays.}}
This code will print string, int, float and bool values in the given DMX to the console. {{todo|Elegant solution to arrays.}}


<source lang=cpp>
<source lang=cpp highlight=48>
#include "cbase.h"
#include "cbase.h"
#include "dmxloader/dmxloader.h"
#include "dmxloader/dmxloader.h"
Line 130: Line 149:
</source>
</source>


[[Category:Glossary]]
====KeyValues2====
dmxloader.lib has several "TextDMX" functions that are not referenced in the SDK's headers. Among them are:
 
<source lang=cpp>
bool UnserializeTextDMX(const char* pFilename,CUtlBuffer & buf,CDmxElement** ppRoot); // fails
bool SerializeTextDMX(const char* pFilename,CUtlBuffer & buf,CDmxElement* pRoot); // produces invalid files
</source>
 
They are both underdeveloped. It's possible that only <code>CDmElement</code> supports KeyValues2.
 
===Third-party libraries===
There are currently two other Datamodel/DMX libraries:
 
* [https://github.com/Artfunkel/Datamodel.NET Datamodel.NET] (CLR/C#)
* [https://github.com/Artfunkel/BlenderSourceTools/blob/master/io_scene_valvesource/datamodel.py datamodel.py] (Python)
* [https://github.com/Jakobg1215/datamodel-rs Datamodel-rs] (Rust)
 
[[Category:File formats]]

Latest revision as of 07:15, 20 May 2025

DMX (Data Model eXchange) stores a Datamodel object, encoded in either binary or ASCII. The internal structure or "format" of a DMX is defined by the user.

Note.pngNote: The SDK tool dmxconvert can convert a DMX between the two encodings.

Header

All DMX files start with this header:

<!-- dmx encoding [encoding name] [encoding version] format [format name] [format version] -->

Encodings

KeyValues2

This ASCII encoding is the successor to KeyValues. It's good if you have a small DMX and want it hand-editable.

Tip.pngTip:When hand-editing you can generate IDs for new elements with this website (or with guidgen itself, if you have the Windows SDK installed).

Binary

Compared to KeyValues2, binary DMX is:

  1. Small, especially when storing large ints or accurate floats
  2. Fast, as values do not have to be converted to or from human-readable characters
  3. Robust, since it does not have to worry about delimiters, line breaks or unclosed quote blocks
  4. Inscrutable, because it contains native binary data

Binary is good for large DMXes.

See DMX/Binary for a technical description of the encoding.

Formats

These are the formats used by Valve.

Particle System

Created by the Particle Editor and read by the engine, usually with the extension pcf. There should be no reason to manipulate these yourself.

Model

The DMX model deprecates SMD in Source 2007 and newer. It can be exported by Valve's official Maya plugin (sourcesdk/maya/2009/plug-ins/valveSource/), the Blender Source Tools and from the 3ds Max pipeline tools in Wall Worm Model Tools.

There are some DMX-related tools in the SDK:

Engine Tools

DMX is widely used to represent data in engine tools. For example, Source Filmmaker save files are stored as DMX, and materials are converted to DMX by Material Editor in order to be edited.

Programming

Handling DMX files has many similarities to handling KeyValues. The big architectural differences are that:

  • Where KVs had keys, Datamodel has CDmxElement objects containing CDmxAttribute (which can point to further elements).
  • There is support for arrays.
  • Attribute values are accessed through a single templated function, not lots of different ones.
  • Each element can have multiple parents. Each element has a 128-bit ID to support this.

Source API

Source has two DMX APIs: CDmxElement and CDmElement. The former is a lightweight implementation designed for use in game code; the latter is designed around the needs of the engine tools and includes heavyweight features like undo levels and element factories. The mod SDK only supports CDmxElement.

For a full list of attribute types, see the bottom of public\datamodel\dmattributetypes.h.

Note.pngNote:Strings are CUtlString objects, not char!

Creating

Icon-Bug.pngBug:The SerializeDMX() overload that requires a file path does not work. You must serialize to a buffer then write out manually.
#include "cbase.h"
#include "dmxloader/dmxloader.h"
#include "dmxloader/dmxelement.h"

CON_COMMAND( dmx_create, "Makes a simple DMX" )
{
	DECLARE_DMX_CONTEXT();
	CDmxElement* DMX = CreateDmxElement("MyElementTypeName");

	DMX->AddAttribute("C++ templates")->SetValue<bool>(true);

	DMX->AddAttribute("Hello")->SetValue("world"); // non-templated shortcut for strings

	DMX->AddAttribute("An_Array")->GetArrayForEdit<float>().AddToTail(3.141);

	CUtlBuffer buf;
	SerializeDMX(buf,DMX);

	FileHandle_t fh = filesystem->Open("test.dmx","w","DEFAULT_WRITE_PATH");
	filesystem->Write(buf.Base(),buf.TellPut(),fh);
	filesystem->Close(fh);
}

Iterator

This code will print string, int, float and bool values in the given DMX to the console.

Todo: Elegant solution to arrays.
#include "cbase.h"
#include "dmxloader/dmxloader.h"
#include "dmxloader/dmxelement.h"

void IterateDmxElement(CDmxElement* pRoot)
{
	for (int i=0;i<pRoot->AttributeCount();i++)
	{
		CDmxAttribute* pCur = pRoot->GetAttribute(i);
		CDmxElement* subElem;
		
		Warning( "%s: ",pCur->GetName() );

		switch (pCur->GetType())
		{
		case AT_ELEMENT:
			subElem = pCur->GetValue<CDmxElement*>();
			if (subElem)
				IterateDmxElement(subElem);
			break;

		case AT_STRING:
			Msg( "STRING | %s\n",pCur->GetValue<CUtlString>().Get() );
			break;
		case AT_INT:
			Msg( "INT | %i\n",pCur->GetValue<int>() );
			break;
		case AT_FLOAT:
			Msg( "FLOAT | %f\n",pCur->GetValue<float>() );
			break;
		case AT_BOOL:
			Msg( "BOOL | %s\n",pCur->GetValue<bool>() ? "true" : "false" );
			break;
		
		default:
			Msg("Unknown type %i\n",pCur->GetType());
			break;
		}
	}
}

CON_COMMAND(dmx_iterate, "Prints a DMX file to the console")
{
	DECLARE_DMX_CONTEXT();
	
	CDmxElement* DMX = (CDmxElement*)DMXAlloc( 50000000 );

	if (UnserializeDMX(args[1],"MOD",false,&DMX))
	{
		IterateDmxElement(DMX);
	}
	else
		Warning("Could not read DMX file %s\n",args[1]);
}

KeyValues2

dmxloader.lib has several "TextDMX" functions that are not referenced in the SDK's headers. Among them are:

bool UnserializeTextDMX(const char* pFilename,CUtlBuffer & buf,CDmxElement** ppRoot); // fails
bool SerializeTextDMX(const char* pFilename,CUtlBuffer & buf,CDmxElement* pRoot); // produces invalid files

They are both underdeveloped. It's possible that only CDmElement supports KeyValues2.

Third-party libraries

There are currently two other Datamodel/DMX libraries: