using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SharpGLTF.Schema2; namespace SharpGLTF.Runtime { // tracks all the disposable objects of a model; // vertex buffers, index buffers, effects and textures. class GraphicsResourceTracker { #region data private readonly List _Disposables = new List(); #endregion #region properties public IReadOnlyList Disposables => _Disposables; #endregion #region API public void AddDisposable(GraphicsResource resource) { if (resource == null) throw new ArgumentNullException(); if (_Disposables.Contains(resource)) throw new ArgumentException(); _Disposables.Add(resource); } #endregion } class TextureFactory { #region lifecycle public TextureFactory(GraphicsDevice device, GraphicsResourceTracker disposables) { _Device = device; _Disposables = disposables; } #endregion #region data private readonly GraphicsDevice _Device; private readonly GraphicsResourceTracker _Disposables; private readonly Dictionary _Textures = new Dictionary(); #endregion #region API public Texture2D UseTexture(Memory.MemoryImage image, string name = null) { if (_Device == null) throw new InvalidOperationException(); if (!image.IsValid) return null; if (_Textures.TryGetValue(image, out Texture2D tex)) return tex; using (var m = image.Open()) { tex = Texture2D.FromStream(_Device, m); _Disposables.AddDisposable(tex); tex.Name = name; _Textures[image] = tex; return tex; } } public Texture2D UseWhiteImage() { const string solidWhitePNg = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFHpUWHRUaXRsZQAACJkrz8gsSQUABoACIippo0oAAAAoelRYdEF1dGhvcgAACJkLy0xOzStJVQhIzUtMSS1WcCzKTc1Lzy8BAG89CQyAoFAQAAAAGklEQVQoz2P8//8/AymAiYFEMKphVMPQ0QAAVW0DHZ8uFaIAAAAASUVORK5CYII="; var toBytes = Convert.FromBase64String(solidWhitePNg); return UseTexture(new ArraySegment(toBytes), "_InternalSolidWhite"); } #endregion } class EffectsFactory { #region lifecycle public EffectsFactory(GraphicsDevice device, GraphicsResourceTracker disposables) { _Device = device; _TexFactory = new TextureFactory(device, disposables); _Disposables = disposables; } #endregion #region data private readonly GraphicsDevice _Device; private readonly TextureFactory _TexFactory; private readonly GraphicsResourceTracker _Disposables; private readonly Dictionary _RigidEffects = new Dictionary(); private readonly Dictionary _SkinnedEffects = new Dictionary(); #endregion #region API - Schema public void Register(Object key, bool isSkinned, Effect effect) { if (key == null) throw new ArgumentNullException(nameof(key)); if (effect == null) throw new ArgumentNullException(nameof(effect)); if (isSkinned && effect is SkinnedEffect skEffect) { _SkinnedEffects[key] = skEffect; } else { _RigidEffects[key] = effect; } } public Effect GetMaterial(Schema2.Material srcMaterial, bool isSkinned) { if (isSkinned) { if (_SkinnedEffects.TryGetValue(srcMaterial, out SkinnedEffect dstMaterial)) return dstMaterial; } else { if (_RigidEffects.TryGetValue(srcMaterial, out Effect dstMaterial)) return dstMaterial; } return null; } internal Texture2D UseTexture(MaterialChannel? channel, string name) { if (!channel.HasValue) return _TexFactory.UseWhiteImage(); if (channel.HasValue && name == null) { name = channel.Value.LogicalParent.Name; if (name == null) name = "null"; name += $"-{channel.Value.Key}"; } return _TexFactory.UseTexture(channel.Value.Texture?.PrimaryImage?.Content ?? default, name); } #endregion } }