DMX: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(hunted down vsdmxio...the truth is out there!)
(→‎Programming: iterator)
Line 28: Line 28:


== Programming ==
== Programming ==
Handling DMX files has many similarities to handling [[KeyValues]]. The two big architectural differences are that:
* <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.
* Values are accessed through a single [[W:Template (programming)|templated]] function, not lots of different ones.
For a full list of attributes, see the bottom of <code>public\datamodel\dmattributetypes.h</code>.
{{note|[[String]]s are <code>[[CUtlString]]</code> objects, not <code>[[char]]</code>!}}
=== Iterator ===
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>
Line 34: Line 47:
#include "dmxloader/dmxelement.h"
#include "dmxloader/dmxelement.h"


CON_COMMAND(test_dmx, "Loads and interprets a DMX file")
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();
DECLARE_DMX_CONTEXT();
CDmxElement* DMX = (CDmxElement*)DMXAlloc( 50000000 );
CDmxElement* DMX = (CDmxElement*)DMXAlloc( 50000000 );
CUtlBuffer buf;


if (UnserializeDMX(args[1],"MOD",false,&DMX))
if (UnserializeDMX(args[1],"MOD",false,&DMX))
{
{
for (int i=0;i<DMX->AttributeCount();i++)
IterateDmxElement(DMX);
{
CDmxAttribute* cur = DMX->GetAttribute(i);
Msg("%s\t%i\n",cur->GetName(),cur->AttributeDataSize(cur->GetType()));
}
 
SerializeDMX(buf,DMX);
// write buf to file
}
}
else
else

Revision as of 12:58, 10 December 2010

DMX (Data Model eXtensible?) is a binary file format used in newer Valve technologies. It appears to be a generic replacement for ASCII formats like SMD or VDF. Currently it has two known uses: Particle systems (.pcf) and model source data (.dmx).

DMX has several advantages over VDF. It is:

  1. Efficient, especially when storing large ints or accurate floats
  2. Generic, and able to store most memory structures
  3. Robust, since it does not have to worry about delimiters, line breaks or unclosed quote blocks

Using DMX

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!

Models

DMX model source files are generated by vsdmxio, which only available publicly as a Maya plugin (sourcesdk/maya/2009/plug-ins/valveSource/). Some editing options are available with the Source 2009 SDK tool dmxedit however, including export to OBJ (unweighted, no animations).

To use dmxedit:

  • Create an empty file called vs.lua in the SDK binaries folder. This is probably meant to contain commands that are executed every time you run the tool.
  • You must then create a LUA file containing the actions you want to perform. Here is a simple example that exports to OBJ:
Load( "<path_to_dmx>" )
Save("<export_path>.obj")
  • Perform dmxedit myfile.lua 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.

Programming

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

  • CDmxElement objects contain either CDmxAttribute objects, which hold actual data, or more CDmxElements. Arrays are possible in both cases.
  • Values are accessed through a single templated function, not lots of different ones.

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

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

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]);
}