Unit tests for VMDL KV3 parsing and bulk editing. Tests parse a sample KV3 VMDL string, verify finding RenderMeshFile and RenderMeshList objects, apply scale and alignment changes via VmdlBulkEditor, test unit conversions for scale, no-op behavior when values match, and reading first entry properties.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ModelPro.Vmdl;
[TestClass]
public class Kv3Tests
{
const string SampleVmdl = """
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:modeldoc30:version{8c2d7a91-9c42-4bf0-883a-5a3b1762d4f1} -->
{
rootNode =
{
_class = "RootNode"
children =
[
{
_class = "RenderMeshList"
children =
[
{
_class = "RenderMeshFile"
name = "Torso_LOD0"
children =
[
{
_class = "RenderMeshMarkup"
use_expensive_tangents = true
},
]
filename = "models/citizen/citizen.fbx"
import_translation = [ 0.0, 0.0, 0.0 ]
import_rotation = [ 0.0, 0.0, 0.0 ]
import_scale = 1.0
align_origin_x_type = "None"
align_origin_y_type = "None"
align_origin_z_type = "None"
parent_bone = ""
import_filter =
{
exclude_by_default = true
exception_list =
[
"CitizenTorso_LOD0",
]
}
},
{
_class = "RenderMeshFile"
name = "Arms_LOD0"
filename = "models/citizen/citizen_arms.fbx"
import_scale = 1.0
align_origin_x_type = "None"
align_origin_y_type = "None"
align_origin_z_type = "None"
},
]
},
]
model_archetype = ""
}
}
""";
[TestMethod]
public void FindsMeshEntries()
{
var doc = Kv3Document.Parse( SampleVmdl );
Assert.AreEqual( 2, doc.FindObjects( "RenderMeshFile" ).Count );
Assert.AreEqual( 1, doc.FindObjects( "RenderMeshList" ).Count );
}
[TestMethod]
public void AppliesScaleAndAlign()
{
var editor = VmdlBulkEditor.Load( SampleVmdl );
Assert.AreEqual( 2, editor.MeshEntryCount );
var props = new MeshEntryProperties
{
ImportScale = 0.5f,
AlignOriginX = AlignOrigin.Center,
AlignOriginY = AlignOrigin.Mins,
AlignOriginZ = AlignOrigin.Maxs
};
Assert.IsTrue( editor.Apply( props ) );
var result = Kv3Document.Parse( editor.Source );
var entries = result.FindObjects( "RenderMeshFile" );
Assert.AreEqual( 2, entries.Count );
foreach ( var entry in entries )
{
Assert.AreEqual( "0.5", (entry.FindField( "import_scale" ).Value as Kv3Scalar)?.Value );
Assert.AreEqual( "Center", (entry.FindField( "align_origin_x_type" ).Value as Kv3Scalar)?.Value );
Assert.AreEqual( "Mins", (entry.FindField( "align_origin_y_type" ).Value as Kv3Scalar)?.Value );
Assert.AreEqual( "Maxs", (entry.FindField( "align_origin_z_type" ).Value as Kv3Scalar)?.Value );
}
}
[TestMethod]
public void ConvertsScaleUnits()
{
// The value in the field is the import_scale multiplier written to the vmdl.
Assert.AreEqual( 1.0f, new ScaleInput { Value = 1, Unit = ScaleUnit.Inches }.ToImportScale(), 0.0001f );
Assert.AreEqual( 0.3937f, new ScaleInput { Value = 0.3937f, Unit = ScaleUnit.Centimeters }.ToImportScale(), 0.0001f );
// Switching 1 inch to cm re-expresses it as 0.3937 (1 cm = 0.3937 inches).
var inches = new ScaleInput { Value = 1, Unit = ScaleUnit.Inches };
var cm = inches.ConvertTo( ScaleUnit.Centimeters );
Assert.AreEqual( 0.3937f, cm.Value, 0.0001f );
Assert.AreEqual( 0.3937f, cm.ToImportScale(), 0.0001f );
// A cm model (import_scale 0.3937) reads back as itself.
var fromCm = ScaleInput.FromImportScale( 0.3937f, ScaleUnit.Centimeters );
Assert.AreEqual( 0.3937f, fromCm.Value, 0.0001f );
Assert.AreEqual( 0.3937f, fromCm.ToImportScale(), 0.0001f );
}
[TestMethod]
public void NoOpWhenValuesMatch()
{
var editor = VmdlBulkEditor.Load( SampleVmdl );
var props = new MeshEntryProperties
{
ImportScale = 1.0f,
AlignOriginX = AlignOrigin.None,
AlignOriginY = AlignOrigin.None,
AlignOriginZ = AlignOrigin.None
};
Assert.IsFalse( editor.Apply( props ) );
Assert.AreEqual( SampleVmdl, editor.Source );
}
[TestMethod]
public void ReadsFirstEntryProperties()
{
var editor = VmdlBulkEditor.Load( SampleVmdl );
var props = editor.ReadFirstEntryProperties();
Assert.AreEqual( 1.0f, props.ImportScale );
Assert.AreEqual( AlignOrigin.None, props.AlignOriginX );
Assert.AreEqual( AlignOrigin.None, props.AlignOriginY );
Assert.AreEqual( AlignOrigin.None, props.AlignOriginZ );
}
}