Editor/RigControlEditor/DmxCameraWriter.cs
using Effigy;
using Marionette;
using Marionette.EditorTools;
using Sandbox;
using System.Collections.Generic;
using System.IO;
namespace Marionette.Tools;
/// <summary>
/// Writes a clip's exported cameras out as a .vdmx - the DMX file a Source 2 tool reads for a
/// camera shot.
///
/// HONESTY ABOUT THIS FILE. The bone-animation DMX writer (DmxAnimWriter) was copied, attribute by
/// attribute, from what the engine's own fbx2dmx.exe emits for a shipping clip - that is the
/// project's standard for "a DMX we are sure the engine loads". There is no fbx2dmx for cameras and
/// no .vdmx anywhere in the install to copy from, so this writer is BUILT TO THE DOCUMENTED SHAPE
/// RATHER THAN COPIED FROM A REFERENCE, and it has not been confirmed to load. Before relying on
/// it, validate it the same way the rest of this format was validated:
///
/// dmxconvert.exe -i <stem>.vdmx -o check.vdmx
///
/// dmxconvert reports the first thing wrong, with a line number, in about a second. The element
/// and attribute spellings below (DmeCamera, transform, fov, znear, zfar) are the working set from
/// Source 2's camera DMX; the header and array name are the two guesses most likely to need
/// adjusting.
/// </summary>
internal static class DmxCameraWriter
{
/// <summary>
/// The cameras as a DMX document: a root element holding one DmeCamera per shot, each with its
/// own inline transform and lens.
/// </summary>
public static string Write( IReadOnlyList<RigCamera> cameras )
{
var w = new DmxText();
w.Raw( "<!-- dmx encoding keyvalues2 1 format camera 1 -->" );
w.Raw( "" );
var idRoot = w.NextId();
w.OpenElement( "DmElement", idRoot, "root" );
w.OpenArray( "cameras", "element_array" );
foreach ( var camera in cameras )
WriteCamera( w, camera );
w.CloseArray();
w.CloseElement();
return w.ToString();
}
public static void WriteFile( string path, IReadOnlyList<RigCamera> cameras ) =>
File.WriteAllText( path, Write( cameras ) );
static void WriteCamera( DmxText w, RigCamera camera )
{
var name = string.IsNullOrWhiteSpace( camera.Name ) ? "camera" : camera.Name;
w.OpenArrayElement( "DmeCamera", w.NextId(), name );
WriteTransform( w, EffigyAnimExport.ToXform( camera.World ) );
w.Attribute( "fov", "float", DmxText.Number( camera.FieldOfView ) );
w.Attribute( "znear", "float", DmxText.Number( camera.ZNear ) );
w.Attribute( "zfar", "float", DmxText.Number( camera.ZFar ) );
w.CloseElement();
}
/// <summary>The camera's transform, inline the same way the model writer hangs a DmeTransform
/// off a joint.</summary>
static void WriteTransform( DmxText w, Xform x )
{
w.OpenAttributeElement( "transform", "DmeTransform", w.NextId(), "transform" );
w.Attribute( "position", "vector3", DmxText.Vector3( x.Origin ) );
w.Attribute( "orientation", "quaternion", DmxText.Quaternion( x ) );
w.CloseElement();
}
}