using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace SharpGLTF.Schema2.LoadAndSave
{
///
/// Test cases for models found in and more....
///
[TestFixture]
[Category("Model Load and Save")]
public class LoadSampleTests
{
#region setup
[OneTimeSetUp]
public void Setup()
{
// TestFiles.DownloadReferenceModels();
}
#endregion
#region helpers
private static void _LoadModel(string f, bool tryFix = false)
{
var perf = System.Diagnostics.Stopwatch.StartNew();
ModelRoot model = null;
try
{
var settings = tryFix ? Validation.ValidationMode.TryFix : Validation.ValidationMode.Strict;
model = ModelRoot.Load(f, settings);
Assert.NotNull(model);
}
catch (Exception ex)
{
TestContext.Progress.WriteLine($"Failed {f.ToShortDisplayPath()}");
Assert.Fail(ex.Message);
}
var perf_load = perf.ElapsedMilliseconds;
// do a model clone and compare it
_AssertAreEqual(model, model.DeepClone());
var perf_clone = perf.ElapsedMilliseconds;
var unsupportedExtensions = new[] { "MSFT_lod", "EXT_lights_image_based" };
// check extensions used
if (unsupportedExtensions.All(uex => !model.ExtensionsUsed.Contains(uex)))
{
var detectedExtensions = model.RetrieveUsedExtensions().ToArray();
CollectionAssert.AreEquivalent(model.ExtensionsUsed, detectedExtensions);
}
// Save models
model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(f), ".obj"));
var perf_wavefront = perf.ElapsedMilliseconds;
model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(f), ".glb"));
var perf_glb = perf.ElapsedMilliseconds;
TestContext.Progress.WriteLine($"processed {f.ToShortDisplayPath()} - Load:{perf_load}ms Clone:{perf_clone}ms S.obj:{perf_wavefront}ms S.glb:{perf_glb}ms");
}
private static void _AssertAreEqual(ModelRoot a, ModelRoot b)
{
var aa = a.GetLogicalChildrenFlattened().ToList();
var bb = b.GetLogicalChildrenFlattened().ToList();
Assert.AreEqual(aa.Count, bb.Count);
CollectionAssert.AreEqual
(
aa.Select(item => item.GetType()),
bb.Select(item => item.GetType())
);
}
#endregion
[TestCase("\\glTF\\")]
// [TestCase("\\glTF-Draco\\")] // Not supported
[TestCase("\\glTF-IBL\\")]
[TestCase("\\glTF-Binary\\")]
[TestCase("\\glTF-Embedded\\")]
[TestCase("\\glTF-pbrSpecularGlossiness\\")]
public void LoadModelsFromKhronosSamples(string section)
{
TestContext.CurrentContext.AttachShowDirLink();
TestContext.CurrentContext.AttachGltfValidatorLinks();
foreach (var f in TestFiles.GetSampleModelsPaths())
{
if (!f.Contains(section)) continue;
_LoadModel(f);
}
}
[Test]
public void LoadModelsFromBabylonJs()
{
TestContext.CurrentContext.AttachShowDirLink();
TestContext.CurrentContext.AttachGltfValidatorLinks();
foreach (var f in TestFiles.GetBabylonJSValidModelsPaths())
{
TestContext.Progress.WriteLine(f);
_LoadModel(f, true);
}
}
[Test]
public void LoadInvalidModelsFromBabylonJs()
{
TestContext.CurrentContext.AttachShowDirLink();
TestContext.CurrentContext.AttachGltfValidatorLinks();
foreach (var f in TestFiles.GetBabylonJSInvalidModelsPaths())
{
TestContext.Progress.WriteLine(f);
try
{
var model = ModelRoot.Load(f);
Assert.Fail("Should throw");
}
catch(Exception ex)
{
TestContext.WriteLine(ex.Message);
}
}
}
[TestCase("SpecGlossVsMetalRough.gltf")]
[TestCase(@"TextureTransformTest.gltf")]
[TestCase(@"UnlitTest\glTF-Binary\UnlitTest.glb")]
public void LoadModelsWithExtensions(string filePath)
{
TestContext.CurrentContext.AttachShowDirLink();
TestContext.CurrentContext.AttachGltfValidatorLinks();
filePath = TestFiles
.GetSampleModelsPaths()
.FirstOrDefault(item => item.EndsWith(filePath));
var model = ModelRoot.Load(filePath);
Assert.NotNull(model);
// do a model clone and compare it
_AssertAreEqual(model, model.DeepClone());
// evaluate and save all the triangles to a Wavefront Object
filePath = System.IO.Path.GetFileNameWithoutExtension(filePath);
model.AttachToCurrentTest(filePath + "_wf.obj");
model.AttachToCurrentTest(filePath + ".glb");
model.AttachToCurrentTest(filePath + ".gltf");
}
[Test]
public void LoadModelWithUnlitMaterial()
{
var f = TestFiles
.GetSampleModelsPaths()
.FirstOrDefault(item => item.EndsWith(@"UnlitTest\glTF-Binary\UnlitTest.glb"));
var model = ModelRoot.Load(f);
Assert.NotNull(model);
Assert.IsTrue(model.LogicalMaterials[0].Unlit);
// do a model roundtrip
var modelBis = ModelRoot.ParseGLB(model.WriteGLB());
Assert.NotNull(modelBis);
Assert.IsTrue(modelBis.LogicalMaterials[0].Unlit);
}
[Test]
public void LoadModelWithLights()
{
var f = TestFiles
.GetSchemaExtensionsModelsPaths()
.FirstOrDefault(item => item.EndsWith("lights.gltf"));
var model = ModelRoot.Load(f);
Assert.NotNull(model);
Assert.AreEqual(3, model.LogicalPunctualLights.Count);
Assert.AreEqual(1, model.DefaultScene.VisualChildren.ElementAt(0).PunctualLight.LogicalIndex);
Assert.AreEqual(0, model.DefaultScene.VisualChildren.ElementAt(1).PunctualLight.LogicalIndex);
}
[Test]
public void LoadModelWithSparseAccessor()
{
var path = TestFiles
.GetSampleModelsPaths()
.FirstOrDefault(item => item.Contains("SimpleSparseAccessor.gltf"));
var model = ModelRoot.Load(path);
Assert.NotNull(model);
var primitive = model.LogicalMeshes[0].Primitives[0];
var accessor = primitive.GetVertexAccessor("POSITION");
var basePositions = accessor._GetMemoryAccessor().AsVector3Array();
var positions = accessor.AsVector3Array();
}
[Test]
public void LoadModelWithMorphTargets()
{
TestContext.CurrentContext.AttachShowDirLink();
var path = TestFiles
.GetSampleModelsPaths()
.FirstOrDefault(item => item.Contains("MorphPrimitivesTest.glb"));
var model = ModelRoot.Load(path);
Assert.NotNull(model);
var triangles = model.DefaultScene
.EvaluateTriangles(null, 0)
.ToArray();
model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(path), ".obj"));
model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(path), ".glb"));
}
[TestCase("RiggedFigure.glb")]
[TestCase("RiggedSimple.glb")]
[TestCase("BoxAnimated.glb")]
[TestCase("AnimatedMorphCube.glb")]
[TestCase("AnimatedMorphSphere.glb")]
[TestCase("CesiumMan.glb")]
//[TestCase("Monster.glb")] // temporarily removed from khronos repo
[TestCase("BrainStem.glb")]
[TestCase("Fox.glb")]
public void LoadModelsWithAnimations(string path)
{
TestContext.CurrentContext.AttachShowDirLink();
path = TestFiles
.GetSampleModelsPaths()
.FirstOrDefault(item => item.Contains(path));
var model = ModelRoot.Load(path);
Assert.NotNull(model);
path = System.IO.Path.GetFileNameWithoutExtension(path);
model.AttachToCurrentTest(path + ".glb");
var triangles = model.DefaultScene
.EvaluateTriangles()
.ToArray();
var anim = model.LogicalAnimations[0];
var duration = anim.Duration;
for(int i=0; i < 10; ++i)
{
var t = duration * i / 10;
int tt = (int)(t * 1000.0f);
model.AttachToCurrentTest($"{path} at {tt}.obj",anim, t);
}
}
[Test]
public void LoadAnimatedMorphCube()
{
TestContext.CurrentContext.AttachShowDirLink();
var path = TestFiles
.GetSampleModelsPaths()
.FirstOrDefault(item => item.Contains("AnimatedMorphCube.glb"));
var model = ModelRoot.Load(path);
Assert.NotNull(model);
var anim = model.LogicalAnimations[0];
var node = model.LogicalNodes[0];
var acc_master = node.Mesh.Primitives[0].GetVertexAccessor("POSITION");
var acc_morph0 = node.Mesh.Primitives[0].GetMorphTargetAccessors(0)["POSITION"];
var acc_morph1 = node.Mesh.Primitives[0].GetMorphTargetAccessors(1)["POSITION"];
var pos_master = acc_master.AsVector3Array();
var pos_morph0 = acc_morph0.AsVector3Array();
var pos_morph1 = acc_morph1.AsVector3Array();
// pos_master
var instance = Runtime.SceneTemplate
.Create(model.DefaultScene, false)
.CreateInstance();
var pvrt = node.Mesh.Primitives[0].GetVertexColumns();
for (float t = 0; t < 5; t+=0.25f)
{
instance.SetAnimationFrame(anim.LogicalIndex, t);
var nodexform = instance.GetDrawableInstance(0).Transform;
TestContext.WriteLine($"Animation at {t}");
if (t < anim.Duration)
{
var mw = anim.GetMorphWeights(node, t);
TestContext.WriteLine($" Morph Weights: {mw[0]} {mw[1]}");
}
var msw = anim.GetSparseMorphWeights(node, t);
TestContext.WriteLine($" Morph Sparse : {msw.Weight0} {msw.Weight1}");
var triangles = model.DefaultScene
.EvaluateTriangles(anim, t)
.ToList();
var vertices = triangles
.SelectMany(item => new[] { item.A.Position, item.B.Position, item.C.Position })
.Distinct()
.ToList();
foreach (var v in vertices) TestContext.WriteLine($"{v}");
TestContext.WriteLine();
}
}
[Test]
public void FindDependencyFiles()
{
TestContext.CurrentContext.AttachShowDirLink();
TestContext.CurrentContext.AttachGltfValidatorLinks();
foreach (var f in TestFiles.GetBabylonJSValidModelsPaths())
{
TestContext.WriteLine(f);
var dependencies = ModelRoot.GetSatellitePaths(f);
foreach(var d in dependencies)
{
TestContext.WriteLine($" {d}");
}
TestContext.WriteLine();
}
}
}
}