/* Copyright (c) 2024-present miku-666 * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1.The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using PckStudio.Core; using PckStudio.Interfaces; namespace PckStudio.ModelSupport { public abstract class ModelImporter where T : class { private Dictionary> _importProviders = new Dictionary>(); private sealed class InternalImportProvider : IModelImportProvider { public string Name => nameof(InternalImportProvider); public FileDialogFilter DialogFilter => _dialogFilter; public bool SupportImport => _import != null; public bool SupportExport => _export != null; private FileDialogFilter _dialogFilter; private Func _import; private Action _export; public InternalImportProvider(FileDialogFilter dialogFilter, Func import, Action export) { _dialogFilter = dialogFilter; _import = import; _export = export; } public void Export(string filename, T model) { _ = _export ?? throw new NotImplementedException(); _export(filename, model); } public T Import(string filename) { _ = _import ?? throw new NotImplementedException(); return _import(filename); } public void Export(ref Stream stream, T model) { throw new NotImplementedException(); } public T Import(Stream stream) { throw new NotImplementedException(); } } /// /// Filter that can be used for or /// public string SupportedModelFileFormatsFilter => string.Join("|", _importProviders.Values.Select(p => p.DialogFilter)); public T Import(string filename) { if (!File.Exists(filename)) { Trace.TraceWarning($"[{nameof(ModelImporter)}:Import] Failed to import '{filename}'. File does not exist."); return default; } if (!HasProvider(filename)) { Trace.TraceWarning($"[{nameof(ModelImporter)}:Import] No provider found for '{Path.GetExtension(filename)}'."); return default; } IModelImportProvider provider = GetProvider(filename); if (!provider.SupportImport) { throw new NotSupportedException($"Provider '{provider.Name}' does not support importing."); } return provider.Import(filename); } public void Export(string filename, T model) { if (model is null) { Trace.TraceError($"[{nameof(ModelImporter)}:Export] Model is null."); return; } if (!HasProvider(filename)) { Trace.TraceWarning($"[{nameof(ModelImporter)}:Export] No provider found for '{Path.GetExtension(filename)}'."); return; } IModelImportProvider provider = GetProvider(filename); if (!provider.SupportExport) { throw new NotSupportedException($"Provider '{provider.Name}' does not support exporting."); } provider.Export(filename, model); } internal bool AddProvider(IModelImportProvider provider) { if (_importProviders.ContainsKey(provider.DialogFilter.Extension)) return false; _importProviders.Add(provider.DialogFilter.Extension, provider); return true; } protected bool InternalAddProvider(FileDialogFilter dialogFilter, Func import, Action export) { return AddProvider(new InternalImportProvider(dialogFilter, import, export)); } /// /// Translates coordinate unit system into our coordinate system /// /// Position/Origin of the Object(Cube). /// The Size of the Object(Cube). /// Describes what axises need translation. /// The translated position protected static Vector3 TransformSpace(Vector3 origin, Vector3 size, Vector3 translationUnit) { // The translation unit describes what axises need to be swapped // Example: // translation unit = (1, 0, 0) => This translation unit will ONLY swap the X axis translationUnit = Vector3.Clamp(translationUnit, Vector3.Zero, Vector3.One); // To better understand see: // https://sharplab.io/#v2:C4LgTgrgdgNAJiA1AHwAICYCMBYAUKgBgAJVMA6AOQgFsBTMASwGMBnAbj1QGYT0iBhIgG88RMb3SjxI3OLlEAbgEMwRBlAAOEYEQC8RKLQDuRAGq0mwAPZguACkwwijogQCUHWfLHLVtAB4aFsC0cHoGxmbBNvYAtC7xTpgeUt6+RGC0LOEAKmBKUCwAYjbU/FY2cOpKISx26lrAKV7epACcdpkszd5i7Z1ZevoBQZahPeIAvqlEM9wkmABsUZYxRHkFxaXlldW1duartmqa2m4zMr2KKhmD+ofWtmT8ADZK1Br1p8BODzFkAC16FZftEngB5QwTbxdIgAKn06E8V1hsXuYK4ZEhtGRvVQAHYiLEurixNNcJMgA Vector3 transformUnit = -((translationUnit * 2) - Vector3.One); Vector3 pos = origin; // The next line essentialy does uses the fomular below just on all axis. // x = -(pos.x + size.x) pos *= transformUnit; pos -= size * translationUnit; return pos; } private bool HasProvider(string filename) { string fileExtension = Path.GetExtension(filename); return _importProviders.ContainsKey(fileExtension) && _importProviders[fileExtension] is not null; } private IModelImportProvider GetProvider(string filename) { string fileExtension = Path.GetExtension(filename); return _importProviders.ContainsKey(fileExtension) ? _importProviders[fileExtension] : null; } } }