using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using NUnit.Framework;
namespace SharpGLTF.Schema2.LoadAndSave
{
///
/// Test cases for models found in
///
[TestFixture]
[AttachmentPathFormat("*/TestResults/LoadAndSave/?", true)]
[Category("Model Load and Save")]
public class LoadSpecialModelsTest
{
#region setup
[OneTimeSetUp]
public void Setup()
{
// TestFiles.DownloadReferenceModels();
}
#endregion
[Test]
public void LoadEscapedUriModel()
{
var path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets\\white space.gltf");
var model = ModelRoot.Load(path);
Assert.That(model, Is.Not.Null);
model.AttachToCurrentTest("white space.glb");
}
public void LoadWithCustomImageLoader()
{
// load Polly model
var model = ModelRoot.Load(TestFiles.GetPollyFileModelPath());
}
[Test(Description = "Example of traversing the visual tree all the way to individual vertices and indices")]
public void LoadPollyModel()
{
// load Polly model
var model = ModelRoot.Load(TestFiles.GetPollyFileModelPath(), Validation.ValidationMode.TryFix);
Assert.That(model, Is.Not.Null);
var triangles = model.DefaultScene
.EvaluateTriangles(null, model.LogicalAnimations[0], 0.5f)
.ToList();
// Save as GLB, and also evaluate all triangles and save as Wavefront OBJ
model.AttachToCurrentTest("polly_out.glb");
model.AttachToCurrentTest("polly_out.obj");
// hierarchically browse some elements of the model:
var scene = model.DefaultScene;
var pollyNode = scene.FindNode(n => n.Name == "Polly_Display");
var pollyPrimitive = pollyNode.Mesh.Primitives[0];
var pollyIndices = pollyPrimitive.GetIndices();
var pollyPositions = pollyPrimitive.GetVertices("POSITION").AsVector3Array();
var pollyNormals = pollyPrimitive.GetVertices("NORMAL").AsVector3Array();
for (int i = 0; i < pollyIndices.Count; i += 3)
{
var a = (int)pollyIndices[i + 0];
var b = (int)pollyIndices[i + 1];
var c = (int)pollyIndices[i + 2];
var ap = pollyPositions[a];
var bp = pollyPositions[b];
var cp = pollyPositions[c];
var an = pollyNormals[a];
var bn = pollyNormals[b];
var cn = pollyNormals[c];
TestContext.WriteLine($"Triangle {ap} {an} {bp} {bn} {cp} {cn}");
}
// create a clone and apply a global axis transform.
var clonedModel = model.DeepClone();
var basisTransform
= Matrix4x4.CreateScale(1, 2, 1)
* Matrix4x4.CreateFromYawPitchRoll(1, 2, 3)
* Matrix4x4.CreateTranslation(10,5,2);
clonedModel.ApplyBasisTransform(basisTransform);
clonedModel.AttachToCurrentTest("polly_out_transformed.glb");
var wsettings = new WriteSettings();
wsettings.ImageWriting = ResourceWriteMode.BufferView;
wsettings.MergeBuffers = true;
wsettings.BuffersMaxSize = 1024 * 1024 * 10;
clonedModel.AttachToCurrentTest("polly_out_merged_10mb.gltf", wsettings);
}
[Test]
public void LoadUniVRM()
{
var path = TestFiles.GetUniVRMModelPath();
var model = ModelRoot.Load(path);
Assert.That(model, Is.Not.Null);
var flattenExtensions = model.GatherUsedExtensions().ToArray();
model.AttachToCurrentTest("AliceModel.glb");
}
// [Test]
public void LoadShrekshaoModel()
{
var path = "Assets\\SpecialCases\\shrekshao.glb";
var model = ModelRoot.Load(path);
Assert.That(model, Is.Not.Null);
}
[Test]
public void LoadMouseModel()
{
// this model has several nodes with curve animations containing a single animation key,
// which is causing some problems to the interpolator.
var path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets\\SpecialCases\\mouse.glb");
var model = ModelRoot.Load(path);
var boundingSphere = Runtime.MeshDecoder.EvaluateBoundingSphere(model.DefaultScene);
var sampler = model
.LogicalNodes[5]
.GetCurveSamplers(model.LogicalAnimations[1])
.Rotation
.CreateCurveSampler(true);
var node5_R_00 = sampler.GetPoint(0);
var node5_R_01 = sampler.GetPoint(1);
Assert.That(node5_R_01, Is.EqualTo(node5_R_00));
model.AttachToCurrentTest("mouse_00.obj", model.LogicalAnimations[1], 0f);
model.AttachToCurrentTest("mouse_01.obj", model.LogicalAnimations[1], 1f);
}
[TestCase("SketchfabExport-WhatIsPBR.glb")] // model has exported tangents in the form <0,0,0,1>
public void LoadSketchfabModels(string path)
{
// this model has several nodes with curve animations containing a single animation key,
// which is causing some problems to the interpolator.
path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, $"Assets\\SpecialCases\\{path}");
var model = ModelRoot.Load(path, Validation.ValidationMode.TryFix);
model.AttachToCurrentTest("output.glb");
}
// these models show normal mapping but lack tangents, which are expected to be
// generated at runtime; These tests generate the tangents and check them against the baseline.
[TestCase("NormalTangentTest.glb")]
[TestCase("NormalTangentMirrorTest.glb")]
public void LoadGeneratedTangetsTest(string fileName)
{
var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.EndsWith(fileName));
var model = ModelRoot.Load(path);
var mesh = model.DefaultScene
.EvaluateTriangles()
.ToMeshBuilder();
var editableScene = new Scenes.SceneBuilder();
editableScene.AddRigidMesh(mesh, Matrix4x4.Identity);
model.AttachToCurrentTest("original.glb");
editableScene.ToGltf2().AttachToCurrentTest("WithTangents.glb");
}
[Test]
public void LoadAndSaveToMemory()
{
var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.EndsWith("Avocado.glb"));
var model = ModelRoot.Load(path);
// model.LogicalImages[0].TransferToSatelliteFile(); // TODO
// we will use this dictionary as our in-memory model container.
var dictionary = new Dictionary>();
// write to dictionary
var wcontext = WriteContext.CreateFromDictionary(dictionary);
model.Save("avocado.gltf", wcontext);
Assert.That(dictionary.ContainsKey("avocado.gltf"), Is.True);
Assert.That(dictionary.ContainsKey("avocado.bin"), Is.True);
// read back from dictionary
var rcontext = ReadContext.CreateFromDictionary(dictionary);
var model2 = ModelRoot.Load("avocado.gltf", rcontext);
// TODO: verify
}
[Test]
public void LoadInvalidModelWithJsonFix()
{
// try to load an invalid gltf with an empty array
var path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets\\SpecialCases\\Invalid_EmptyArray.gltf");
Assert.Throws(() => ModelRoot.Load(path));
// try to load an invalid gltf with an empty array, using a hook to fix the json before running the parser.
var rsettings = new ReadSettings();
rsettings.JsonPreprocessor = _RemoveEmptyArrayJsonProcessor;
var model = ModelRoot.Load(path, rsettings);
Assert.That(model, Is.Not.Null);
// save the model, using a hook to modify the json before writing it to the file.
var wsettings = new WriteSettings();
wsettings.JsonPostprocessor = json =>
{
json = json.Replace("glTF 2.0 Validator test suite", "postprocessed json"); return json;
};
path = model.AttachToCurrentTest("modified.gltf", wsettings);
model = ModelRoot.Load(path);
Assert.That("postprocessed json", Is.EqualTo(model.Asset.Generator));
}
private string _RemoveEmptyArrayJsonProcessor(string json)
{
var obj = Newtonsoft.Json.Linq.JObject.Parse(json);
var children = obj.Children().ToArray();
children[1].Remove(); // remove the empty "meshes" array.
json = obj.ToString();
return json;
}
}
}