Rename 'ModelImporter' -> 'SkinModelImporter' and add api interface to add custom import/export providers

This commit is contained in:
miku-666
2024-08-06 14:05:40 +02:00
parent f5aa86f055
commit 0d8704c00a
8 changed files with 143 additions and 49 deletions

View File

@@ -181,20 +181,20 @@ namespace PckStudio.Forms.Editor
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Title = "Save Model File";
saveFileDialog.Filter = ModelImporter.SupportedModelFileFormatsFilter;
saveFileDialog.Filter = SkinModelImporter.SupportedModelFileFormatsFilter;
saveFileDialog.FileName = _skin.MetaData.Name.TrimEnd(new char[] { '\n', '\r' }).Replace(' ', '_');
if (saveFileDialog.ShowDialog() == DialogResult.OK)
ModelImporter.Export(saveFileDialog.FileName, _skin.Model);
SkinModelImporter.Export(saveFileDialog.FileName, _skin.Model);
}
private void importSkinButton_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Title = "Select Model File";
openFileDialog.Filter = ModelImporter.SupportedModelFileFormatsFilter;
openFileDialog.Filter = SkinModelImporter.SupportedModelFileFormatsFilter;
if (MessageBox.Show("Import custom model project file? Your current work will be lost!", "", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) == DialogResult.Yes && openFileDialog.ShowDialog() == DialogResult.OK)
{
SkinModelInfo modelInfo = ModelImporter.Import(openFileDialog.FileName);
SkinModelInfo modelInfo = SkinModelImporter.Import(openFileDialog.FileName);
if (modelInfo is not null)
{
_skin.Model = modelInfo;

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PckStudio.Internal.Skin;
using PckStudio.Internal;
namespace PckStudio.Interfaces
{
internal interface IModelImportProvider<T> where T : class
{
public string Name { get; }
public FileDialogFilter DialogFilter { get; }
public T Import(string filename);
public T Import(Stream stream);
public void Export(string filename, T model);
public void Export(ref Stream stream, T model);
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PckStudio.Internal;
using PckStudio.Internal.Skin;
namespace PckStudio.Interfaces
{
internal interface ISkinModelImportProvider : IModelImportProvider<SkinModelInfo>
{
}
}

View File

@@ -46,6 +46,7 @@ namespace PckStudio.Internal.App
_ = Tiles.MoonPhaseImageList;
_ = Tiles.PaintingImageList;
SettingsManager.Initialize();
SkinModelImporter.Initialize();
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
Task.Run(GetContributors);
}

View File

@@ -1,10 +1,14 @@
namespace PckStudio.Internal
using System.IO;
namespace PckStudio.Internal
{
internal readonly struct FileDialogFilter
{
public readonly string Description;
public readonly string Pattern;
public string Extension => Path.GetExtension(Pattern);
public FileDialogFilter(string description, string pattern)
{
Description = description;

View File

@@ -37,67 +37,105 @@ using OMI.Formats.Model;
using PckStudio.Internal.Json;
using System.Collections.ObjectModel;
using PckStudio.Properties;
using PckStudio.Interfaces;
namespace PckStudio.Internal
{
internal static class ModelImporter
internal static class SkinModelImporter
{
internal static FileDialogFilter[] SupportedModelFormts { get; } =
[
new ("Pck skin model(*.psm)", "*.psm"),
new ("Block bench model(*.bbmodel)", "*.bbmodel"),
new ("Bedrock (Legacy) Model(*.geo.json;*.json)", "*.geo.json;*.json"),
];
private static Dictionary<string, ISkinModelImportProvider> _importProviders = new Dictionary<string, ISkinModelImportProvider>();
internal static string SupportedModelFileFormatsFilter { get; } = string.Join("|", SupportedModelFormts);
private sealed class SimpleSkinImportProvider : ISkinModelImportProvider
{
public string Name => nameof(SimpleSkinImportProvider);
public FileDialogFilter DialogFilter => _dialogFilter;
private FileDialogFilter _dialogFilter;
private Func<string, SkinModelInfo> _import;
private Action<string, SkinModelInfo> _export;
public SimpleSkinImportProvider(FileDialogFilter dialogFilter, Func<string, SkinModelInfo> import, Action<string, SkinModelInfo> export)
{
_dialogFilter = dialogFilter;
_import = import;
_export = export;
}
public void Export(string filename, SkinModelInfo model)
{
_ = _export ?? throw new NotImplementedException();
_export(filename, model);
}
public SkinModelInfo Import(string filename)
{
_ = _import ?? throw new NotImplementedException();
return _import(filename);
}
public void Export(ref Stream stream, SkinModelInfo model)
{
throw new NotImplementedException();
}
public SkinModelInfo Import(Stream stream)
{
throw new NotImplementedException();
}
}
internal static string SupportedModelFileFormatsFilter => string.Join("|", _importProviders.Values.Select(p => p.DialogFilter));
internal static ReadOnlyDictionary<string, JsonModelMetaData> ModelTextureLocations { get; private set; }
static ModelImporter()
internal static void Initialize()
{
ModelTextureLocations = JsonConvert.DeserializeObject<ReadOnlyDictionary<string, JsonModelMetaData>>(Resources.modelTextureLocations);
InternalAddProvider(new("Pck skin model(*.psm)", "*.psm"), ImportPsm, ExportPsm);
InternalAddProvider(new("Block bench model(*.bbmodel)", "*.bbmodel"), ImportBlockBenchModel, ExportBlockBenchModel);
InternalAddProvider(new("Bedrock (Legacy) Model(*.geo.json;*.json)", "*.geo.json;*.json"), ImportBedrockJson, ExportBedrockJson);
}
internal static bool AddProvider(ISkinModelImportProvider provider)
{
if (_importProviders.ContainsKey(provider.DialogFilter.Extension))
return false;
_importProviders.Add(provider.DialogFilter.Extension, provider);
return true;
}
private static bool InternalAddProvider(FileDialogFilter dialogFilter, Func<string, SkinModelInfo> import, Action<string, SkinModelInfo> export)
{
if (import == null || export == null)
return false;
return AddProvider(new SimpleSkinImportProvider(dialogFilter, import, export));
}
internal static SkinModelInfo Import(string fileName)
{
string fileExtension = Path.GetExtension(fileName);
switch (fileExtension)
{
case ".psm":
return ImportPsm(fileName);
case ".bbmodel":
return ImportBlockBenchModel(fileName);
case ".json":
return ImportBedrockJson(fileName);
default:
Trace.TraceWarning($"[{nameof(ModelImporter)}:Import] Model file format: '{fileExtension}' is not supported.");
return null;
}
if (_importProviders.ContainsKey(fileExtension) && _importProviders[fileExtension] is not null)
return _importProviders[fileExtension].Import(fileName);
Trace.TraceWarning($"[{nameof(SkinModelImporter)}:Import] No provider found for '{fileExtension}'.");
return null;
}
internal static void Export(string fileName, SkinModelInfo model)
{
if (model is null)
{
Trace.TraceError($"[{nameof(ModelImporter)}:Export] Model is null.");
Trace.TraceError($"[{nameof(SkinModelImporter)}:Export] Model is null.");
return;
}
string fileExtension = Path.GetExtension(fileName);
switch (fileExtension)
{
case ".psm":
ExportPsm(fileName, model);
break;
case ".bbmodel":
ExportBlockBenchModel(fileName, model);
break;
case ".json":
ExportBedrockJson(fileName, model);
break;
default:
Trace.TraceWarning($"[{nameof(ModelImporter)}:Export] Model file format: '{fileExtension}' is not supported.");
return;
}
if (_importProviders.ContainsKey(fileExtension) && _importProviders[fileExtension] is not null)
_importProviders[fileExtension].Export(fileName, model);
Trace.TraceWarning($"[{nameof(SkinModelImporter)}:Export] No provider found for '{fileExtension}'.");
}
internal static SkinModelInfo ImportPsm(string fileName)
@@ -256,17 +294,27 @@ namespace PckStudio.Internal
{
BlockBenchModel blockBenchModel = CreateBlockBenchModel(Path.GetFileNameWithoutExtension(fileName), model.TextureSize, textures.Select(nt => (Texture)nt));
List<Outline> outliners = new List<Outline>(5);
Dictionary<string, Outline> outliners = new Dictionary<string, Outline>(5);
List<Element> elements = new List<Element>(model.Parts.Count);
Vector3 transformAxis = new Vector3(1, 1, 0);
Outline GetOrCreateOutline(string partName)
{
if (!outliners.ContainsKey(partName))
outliners.Add(partName, new Outline(partName));
return outliners[partName];
}
foreach (ModelPart part in model.Parts.Values)
{
//Outline outline = GetOrCreateOutline(part.Name);
var outline = new Outline(part.Name);
Vector3 partTranslation = part.Translation;
outline.Origin = TranslateToInternalPosition("", partTranslation, Vector3.Zero, transformAxis);
outline.Origin = TransformSpace(partTranslation, Vector3.Zero, transformAxis);
outline.Origin += Vector3.UnitY * 24f;
Vector3 rotation = part.Rotation + part.AdditionalRotation;
outline.Rotation = rotation * TransformSpace(Vector3.One, Vector3.Zero, transformAxis);
@@ -278,7 +326,7 @@ namespace PckStudio.Internal
elements.Add(element);
outline.Children.Add(element.Uuid);
}
outliners.Add(outline);
outliners.Add(part.Name, outline);
}
blockBenchModel.Elements = elements.ToArray();

View File

@@ -632,10 +632,10 @@ namespace PckStudio
IEnumerable<NamedTexture> GetModelTextures(string modelName)
{
if (!ModelImporter.ModelTextureLocations.ContainsKey(modelName) || ModelImporter.ModelTextureLocations[modelName]?.TextureLocations?.Length <= 0)
if (!SkinModelImporter.ModelTextureLocations.ContainsKey(modelName) || SkinModelImporter.ModelTextureLocations[modelName]?.TextureLocations?.Length <= 0)
return Array.Empty<NamedTexture>();
return ModelImporter.ModelTextureLocations[modelName].TextureLocations.Select(texturePath =>
return SkinModelImporter.ModelTextureLocations[modelName].TextureLocations.Select(texturePath =>
{
if (currentPCK.TryGetAsset(texturePath + ".png", PckAssetType.TextureFile, out PckAsset modelTextureAsset) ||
currentPCK.TryGetAsset(texturePath + ".tga", PckAssetType.TextureFile, out modelTextureAsset))
@@ -648,7 +648,7 @@ namespace PckStudio
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
ModelImporter.ExportBlockBenchModel(openFileDialog.FileName, model, textures);
SkinModelImporter.ExportBlockBenchModel(openFileDialog.FileName, model, textures);
}
}
}

View File

@@ -150,6 +150,8 @@
<DependentUpon>ContributorsForm.cs</DependentUpon>
</Compile>
<Compile Include="Extensions\OpenTkMatrixExtensions.cs" />
<Compile Include="Interfaces\IModelImportProvider.cs" />
<Compile Include="Interfaces\ISkinModelImportProvider.cs" />
<Compile Include="Internal\IO\PSM\PSMFileReader.cs" />
<Compile Include="Internal\IO\PSM\PSMFileWriter.cs" />
<Compile Include="Internal\GameConstants.cs" />
@@ -167,7 +169,7 @@
<Compile Include="Internal\FileDialogFilter.cs" />
<Compile Include="Internal\Deserializer\ImageDeserializer.cs" />
<Compile Include="Internal\Misc\RichPresenceClient.cs" />
<Compile Include="Internal\ModelImporter.cs" />
<Compile Include="Internal\SkinModelImporter.cs" />
<Compile Include="Internal\ModelPartSpecifics.cs" />
<Compile Include="Internal\Json\JsonModelMetaData.cs" />
<Compile Include="Internal\NamedTexture.cs" />