using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MODELMESH = SharpGLTF.Runtime.Template.RuntimeModelMesh; namespace SharpGLTF.Runtime.Template { public class MonoGameModelTemplate { #region lifecycle internal MonoGameModelTemplate(SceneTemplate[] scenes, int defaultSceneIndex, IReadOnlyDictionary meshes) { _Meshes = meshes; _Effects = _Meshes.Values .SelectMany(item => item.Effects) .Distinct() .ToArray(); _Scenes = scenes; #pragma warning disable GLTFRT1000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. _Bounds = scenes.Select(item => new BoundingSphere(item.SphereBounds.center, item.SphereBounds.radius)).ToArray(); #pragma warning restore GLTFRT1000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. /* _Bounds = scenes .Select(item => CalculateBounds(item)) .ToArray();*/ _DefaultSceneIndex = defaultSceneIndex; } #endregion #region data /// /// Meshes shared by all the scenes. /// internal readonly IReadOnlyDictionary _Meshes; /// /// Effects shared by all the meshes. /// private readonly Effect[] _Effects; private readonly SceneTemplate[] _Scenes; private readonly BoundingSphere[] _Bounds; private readonly int _DefaultSceneIndex; #endregion #region properties public int SceneCount => _Scenes.Length; public IReadOnlyList Effects => _Effects; public BoundingSphere Bounds => GetBounds(_DefaultSceneIndex); #endregion #region API public int IndexOfScene(string sceneName) => Array.FindIndex(_Scenes, item => item.Name == sceneName); public BoundingSphere GetBounds(int sceneIndex) => _Bounds[sceneIndex]; /// /// Creates an instance from the default model's scene. /// /// A model instance. public MonoGameModelInstance CreateInstance() => CreateInstance(_DefaultSceneIndex); /// /// Creates an instance from the given model scene. /// /// The scene index. /// A model instance. public MonoGameModelInstance CreateInstance(int sceneIndex) { return new MonoGameModelInstance(this, _Scenes[sceneIndex].CreateInstance()); } private BoundingSphere CalculateBounds(SceneTemplate scene) { var instances = scene.CreateInstance(); var bounds = default(BoundingSphere); foreach (var inst in instances) { var b = _Meshes[inst.Template.LogicalMeshIndex].BoundingSphere; System.Diagnostics.Debug.Assert(b.Radius > 0); switch(inst.Transform) { case Transforms.RigidTransform statXform: b = b.Transform(statXform.WorldMatrix); break; case Transforms.SkinnedTransform skinXform: // this is a bit agressive and probably over-reaching, but with skins you // never know the actual bounds unless you calculate them frame by frame. var bb = b; foreach (var xb in skinXform.SkinMatrices.Select(item => bb.Transform(item))) { b = BoundingSphere.CreateMerged(b, xb); } break; } bounds = bounds.Radius == 0 ? b : BoundingSphere.CreateMerged(bounds, b); } return bounds; } #endregion #region shared lights and effects public void ConfigureLightsAndFog(Action configureLights, Action configureFog) { configureLights ??= l => l.EnableDefaultLighting(); configureFog ??= f => f.FogEnabled = false; foreach (var mesh in _Meshes.Values) { foreach (var effect in _Effects) { if (effect is IEffectLights lights) { configureLights.Invoke(lights); } if (effect is IEffectFog fog) { configureFog.Invoke(fog); } } } } #endregion } }