mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-05-23 06:54:54 +00:00
Rename 'ModelImporter' -> 'SkinModelImporter' and add api interface to add custom import/export providers
This commit is contained in:
@@ -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;
|
||||
|
||||
24
PCK-Studio/Interfaces/IModelImportProvider.cs
Normal file
24
PCK-Studio/Interfaces/IModelImportProvider.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
15
PCK-Studio/Interfaces/ISkinModelImportProvider.cs
Normal file
15
PCK-Studio/Interfaces/ISkinModelImportProvider.cs
Normal 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>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ namespace PckStudio.Internal.App
|
||||
_ = Tiles.MoonPhaseImageList;
|
||||
_ = Tiles.PaintingImageList;
|
||||
SettingsManager.Initialize();
|
||||
SkinModelImporter.Initialize();
|
||||
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
Task.Run(GetContributors);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user