mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-07-07 15:18:07 +00:00
PckStudio - Fix typo in csproj file
This commit is contained in:
23
PckStudio.ModelSupport/Extension/SkinExtension.cs
Normal file
23
PckStudio.ModelSupport/Extension/SkinExtension.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OMI.Formats.Model;
|
||||
using PckStudio.Core.Skin;
|
||||
using PckStudio.ModelSupport;
|
||||
|
||||
namespace PckStuido.ModelSupport.Extension
|
||||
{
|
||||
public static class SkinExtension
|
||||
{
|
||||
public static SkinModelInfo GetModelInfo(this Skin skin) => new SkinModelInfo(skin.Texture, skin.Anim, skin.Model);
|
||||
|
||||
public static void SetModelInfo(this Skin skin, SkinModelInfo modelInfo)
|
||||
{
|
||||
skin.Texture = modelInfo.Texture;
|
||||
skin.Anim = modelInfo.Anim;
|
||||
skin.Model = modelInfo.Model;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
PckStudio.ModelSupport/Format/External/BedrockLegacyModel.cs
vendored
Normal file
13
PckStudio.ModelSupport/Format/External/BedrockLegacyModel.cs
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PckStudio.ModelSupport.Format.External
|
||||
{
|
||||
internal class BedrockLegacyModel : Dictionary<string, Geometry>
|
||||
{
|
||||
}
|
||||
}
|
||||
158
PckStudio.ModelSupport/Format/External/BedrockModel.cs
vendored
Normal file
158
PckStudio.ModelSupport/Format/External/BedrockModel.cs
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PckStudio.ModelSupport.Format.External
|
||||
{
|
||||
internal class BedrockModel
|
||||
{
|
||||
[JsonProperty("format_version")]
|
||||
public string FormatVersion { get; set; }
|
||||
|
||||
[JsonProperty("minecraft:geometry")]
|
||||
public List<Geometry> Models { get; } = new List<Geometry>();
|
||||
}
|
||||
|
||||
internal class Geometry
|
||||
{
|
||||
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public GeometryDescription Description { get; set; }
|
||||
|
||||
[JsonProperty("bones")]
|
||||
public List<Bone> Bones { get; } = new List<Bone>();
|
||||
}
|
||||
|
||||
internal class GeometryDescription
|
||||
{
|
||||
[JsonProperty("identifier")]
|
||||
public string Identifier { get; set; }
|
||||
|
||||
[JsonProperty("texture_width")]
|
||||
private int TextureWidth;
|
||||
|
||||
[JsonProperty("texture_height")]
|
||||
private int TextureHeight;
|
||||
|
||||
[JsonIgnore]
|
||||
public Size TextureSize
|
||||
{
|
||||
get => new Size(TextureWidth, TextureHeight);
|
||||
set
|
||||
{
|
||||
TextureWidth = value.Width;
|
||||
TextureHeight = value.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Bone
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
public Bone(string name)
|
||||
{
|
||||
Name = name;
|
||||
Cubes = new List<Cube>();
|
||||
}
|
||||
|
||||
|
||||
[JsonProperty("parent", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Parent { get; set; } = "";
|
||||
|
||||
[JsonIgnore]
|
||||
public Vector3 Pivot
|
||||
{
|
||||
get => pivot.Length < 3 ? Vector3.Zero : new Vector3(pivot[0], pivot[1], pivot[2]);
|
||||
set
|
||||
{
|
||||
if (pivot.Length < 3)
|
||||
pivot = new float[3];
|
||||
pivot[0] = value.X;
|
||||
pivot[1] = value.Y;
|
||||
pivot[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("cubes")]
|
||||
public List<Cube> Cubes;
|
||||
|
||||
[JsonProperty("pivot")]
|
||||
private float[] pivot { get; set; } = new float[3];
|
||||
}
|
||||
|
||||
internal class Cube
|
||||
{
|
||||
[JsonProperty("origin")]
|
||||
private float[] origin { get; set; } = new float[3];
|
||||
[JsonIgnore]
|
||||
public Vector3 Origin
|
||||
{
|
||||
get => origin.Length < 3 ? Vector3.Zero : new Vector3(origin[0], origin[1], origin[2]);
|
||||
set
|
||||
{
|
||||
if (origin.Length < 3)
|
||||
origin = new float[3];
|
||||
origin[0] = value.X;
|
||||
origin[1] = value.Y;
|
||||
origin[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("rotation")]
|
||||
private float[] rotation { get; set; } = new float[3];
|
||||
[JsonIgnore]
|
||||
public Vector3 Rotation
|
||||
{
|
||||
get => rotation.Length < 3 ? Vector3.Zero : new Vector3(rotation[0], rotation[1], rotation[2]);
|
||||
set
|
||||
{
|
||||
rotation[0] = value.X;
|
||||
rotation[1] = value.Y;
|
||||
rotation[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("size")]
|
||||
private float[] size { get; set; } = new float[3];
|
||||
[JsonIgnore]
|
||||
public Vector3 Size
|
||||
{
|
||||
get => size.Length < 3 ? Vector3.Zero : new Vector3(size[0], size[1], size[2]);
|
||||
set
|
||||
{
|
||||
if (size.Length < 3)
|
||||
size = new float[3];
|
||||
size[0] = value.X;
|
||||
size[1] = value.Y;
|
||||
size[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("uv")]
|
||||
private float[] uv { get; set; } = new float[2];
|
||||
[JsonIgnore]
|
||||
public Vector2 Uv
|
||||
{
|
||||
get => uv.Length < 2 ? Vector2.Zero : new Vector2(uv[0], uv[1]);
|
||||
set
|
||||
{
|
||||
if (uv.Length < 2)
|
||||
uv = new float[2];
|
||||
uv[0] = value.X;
|
||||
uv[1] = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("inflate")]
|
||||
public float Inflate { get; set; } = 0f;
|
||||
|
||||
[JsonProperty("mirror", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public bool Mirror { get; set; } = false;
|
||||
}
|
||||
}
|
||||
362
PckStudio.ModelSupport/Format/External/BlockBenchModel.cs
vendored
Normal file
362
PckStudio.ModelSupport/Format/External/BlockBenchModel.cs
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NamedTexture = PckStudio.Core.NamedData<System.Drawing.Image>;
|
||||
|
||||
namespace PckStudio.ModelSupport.Format.External
|
||||
{
|
||||
internal static class BlockBenchFormatInfos
|
||||
{
|
||||
internal static readonly string FormatVersion = "4.5";
|
||||
|
||||
internal static BlockBenchFormatInfo Free { get; } = new BlockBenchFormatInfo(FormatVersion, "free", true);
|
||||
internal static BlockBenchFormatInfo BedrockEntity { get; } = new BlockBenchFormatInfo(FormatVersion, "bedrock", true);
|
||||
}
|
||||
|
||||
internal sealed class BlockBenchFormatInfo
|
||||
{
|
||||
[JsonProperty("format_version")]
|
||||
internal string FormatVersion { get; }
|
||||
|
||||
[JsonProperty("model_format")]
|
||||
internal string ModelFormat { get; }
|
||||
|
||||
[JsonProperty("box_uv")]
|
||||
internal bool UseBoxUv { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
private BlockBenchFormatInfo() { }
|
||||
|
||||
internal BlockBenchFormatInfo(string formatVersion, string modelFormat, bool useBoxUv)
|
||||
{
|
||||
FormatVersion = formatVersion;
|
||||
ModelFormat = modelFormat;
|
||||
UseBoxUv = useBoxUv;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Element
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
internal string Name;
|
||||
|
||||
[JsonProperty("box_uv")]
|
||||
internal bool UseBoxUv;
|
||||
|
||||
[JsonProperty("visibility", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
internal bool IsVisibile { get; set; } = true;
|
||||
|
||||
[JsonProperty("rescale")]
|
||||
internal bool Rescale;
|
||||
|
||||
[JsonProperty("mirror_uv")]
|
||||
internal bool MirrorUv;
|
||||
|
||||
[JsonProperty("locked")]
|
||||
internal bool Locked;
|
||||
|
||||
[DefaultValue(true)]
|
||||
[JsonProperty("export", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
internal bool Export { get; } = true;
|
||||
|
||||
[JsonProperty("inflate")]
|
||||
internal float Inflate;
|
||||
|
||||
[JsonProperty("origin", NullValueHandling = NullValueHandling.Ignore)]
|
||||
private float[] origin;
|
||||
|
||||
[JsonProperty("from")]
|
||||
private float[] from;
|
||||
|
||||
[JsonProperty("to")]
|
||||
private float[] to;
|
||||
|
||||
[JsonProperty("uv_offset")]
|
||||
private int[] uv_offset;
|
||||
|
||||
[JsonProperty("rotation", NullValueHandling = NullValueHandling.Ignore)]
|
||||
private float[] rotation;
|
||||
|
||||
[JsonIgnore()]
|
||||
internal Vector3 Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(origin?[0] ?? 0, origin?[1] ?? 0, origin?[2] ?? 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (origin is null || origin.Length < 3)
|
||||
origin = new float[3];
|
||||
origin[0] = value.X;
|
||||
origin[1] = value.Y;
|
||||
origin[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore()]
|
||||
internal Vector3 From
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(from?[0] ?? 0, from?[1] ?? 0, from?[2] ?? 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (from is null || from.Length < 3)
|
||||
from = new float[3];
|
||||
from[0] = value.X;
|
||||
from[1] = value.Y;
|
||||
from[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore()]
|
||||
internal Vector3 To
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(to?[0] ?? 0, to?[1] ?? 0, to?[2] ?? 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (to is null || to.Length < 3)
|
||||
to = new float[3];
|
||||
to[0] = value.X;
|
||||
to[1] = value.Y;
|
||||
to[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore()]
|
||||
internal Vector2 UvOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2(uv_offset?[0] ?? 0, uv_offset?[1] ?? 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (uv_offset is null || uv_offset.Length < 2)
|
||||
uv_offset = new int[2];
|
||||
uv_offset[0] = (int)value.X;
|
||||
uv_offset[1] = (int)value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore()]
|
||||
internal Vector3 Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(rotation?[0] ?? 0, rotation?[1] ?? 0, rotation?[2] ?? 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (rotation is null || rotation.Length < 3)
|
||||
rotation = new float[3];
|
||||
rotation[0] = value.X;
|
||||
rotation[1] = value.Y;
|
||||
rotation[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("type")]
|
||||
internal string Type;
|
||||
|
||||
[JsonProperty("uuid")]
|
||||
internal Guid Uuid;
|
||||
|
||||
internal static Element CreateCube(string name, Vector2 uvOffset, Vector3 pos, Vector3 size, float inflate, bool mirror)
|
||||
{
|
||||
return new Element
|
||||
{
|
||||
Name = name,
|
||||
UseBoxUv = true,
|
||||
Locked = false,
|
||||
Rescale = false,
|
||||
Type = "cube",
|
||||
Uuid = Guid.NewGuid(),
|
||||
UvOffset = uvOffset,
|
||||
MirrorUv = mirror,
|
||||
Inflate = inflate,
|
||||
From = pos,
|
||||
To = pos + size
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class Texture
|
||||
{
|
||||
public static implicit operator Image(Texture texture) => texture.GetImage();
|
||||
public static implicit operator Texture(Image image) => new Texture(image);
|
||||
public static implicit operator Texture(NamedTexture namedTexture) => new Texture(namedTexture.Name, namedTexture.Value);
|
||||
|
||||
private const string _TEXTUREDATAHEAD = "data:image/png;base64,";
|
||||
|
||||
[JsonConstructor]
|
||||
private Texture()
|
||||
{
|
||||
}
|
||||
|
||||
internal Texture(string name, Image image)
|
||||
: this(image)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
internal Texture(Image image)
|
||||
{
|
||||
if (image is not null)
|
||||
{
|
||||
SetImage(image);
|
||||
return;
|
||||
}
|
||||
Debug.WriteLine($"param: {nameof(image)} is null");
|
||||
}
|
||||
|
||||
[JsonProperty("name")]
|
||||
internal string Name { get; set; }
|
||||
|
||||
[JsonProperty("source")]
|
||||
internal string TextureSource { get; private set; }
|
||||
|
||||
private Image GetImage()
|
||||
{
|
||||
string data = TextureSource;
|
||||
if (data.StartsWith(_TEXTUREDATAHEAD))
|
||||
{
|
||||
byte[] encodedData = Convert.FromBase64String(data.Substring(_TEXTUREDATAHEAD.Length));
|
||||
using var ms = new MemoryStream(encodedData);
|
||||
return Image.FromStream(ms);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetImage(Image image)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
image.Save(ms, ImageFormat.Png);
|
||||
TextureSource = _TEXTUREDATAHEAD + Convert.ToBase64String(ms.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
internal class Outline
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
internal string Name;
|
||||
|
||||
[JsonProperty("origin")]
|
||||
private float[] origin;
|
||||
|
||||
[JsonIgnore]
|
||||
public Vector3 Origin
|
||||
{
|
||||
get => new Vector3(origin?[0] ?? 0, origin?[1] ?? 0, origin?[2] ?? 0);
|
||||
set
|
||||
{
|
||||
if (origin is null || origin.Length < 3)
|
||||
origin = new float[3];
|
||||
origin[0] = value.X;
|
||||
origin[1] = value.Y;
|
||||
origin[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("rotation")]
|
||||
private float[] rotation;
|
||||
|
||||
[JsonIgnore]
|
||||
public Vector3 Rotation
|
||||
{
|
||||
get => new Vector3(rotation?[0] ?? 0, rotation?[1] ?? 0, rotation?[2] ?? 0);
|
||||
set
|
||||
{
|
||||
if (rotation is null || rotation.Length < 3)
|
||||
rotation = new float[3];
|
||||
rotation[0] = value.X;
|
||||
rotation[1] = value.Y;
|
||||
rotation[2] = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("uuid")]
|
||||
internal Guid Uuid;
|
||||
|
||||
[JsonProperty("children")]
|
||||
internal JArray Children;
|
||||
|
||||
public Outline(string name)
|
||||
{
|
||||
Name = name;
|
||||
origin = new float[3];
|
||||
Uuid = Guid.NewGuid();
|
||||
Children = new JArray();
|
||||
}
|
||||
}
|
||||
|
||||
internal class TextureRes
|
||||
{
|
||||
[JsonProperty("width")]
|
||||
internal int Width { get; set; }
|
||||
|
||||
[JsonProperty("height")]
|
||||
internal int Height { get; set; }
|
||||
|
||||
public TextureRes(int width, int height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public static implicit operator Size(TextureRes res) => new Size(res.Width, res.Height);
|
||||
public static implicit operator TextureRes(Size size) => new TextureRes(size.Width, size.Height);
|
||||
}
|
||||
|
||||
internal class BlockBenchModel
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
internal string Name;
|
||||
|
||||
[JsonProperty("meta")]
|
||||
internal BlockBenchFormatInfo Format;
|
||||
|
||||
[JsonProperty("model_identifier")]
|
||||
internal string ModelIdentifier { get; set; } = "";
|
||||
|
||||
[JsonProperty("resolution")]
|
||||
internal TextureRes TextureResolution;
|
||||
|
||||
[JsonProperty("elements")]
|
||||
internal Element[] Elements;
|
||||
|
||||
[JsonProperty("outliner")]
|
||||
internal JArray Outliner;
|
||||
|
||||
[JsonProperty("textures")]
|
||||
internal Texture[] Textures;
|
||||
|
||||
internal static BlockBenchModel Create(BlockBenchFormatInfo formatInfo, string name, Size textureResolution, IEnumerable<Texture> textures)
|
||||
{
|
||||
return new BlockBenchModel()
|
||||
{
|
||||
Name = name,
|
||||
Textures = textures.ToArray(),
|
||||
TextureResolution = textureResolution,
|
||||
ModelIdentifier = "",
|
||||
Format = formatInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
86
PckStudio.ModelSupport/Format/Internal/PSM/PSMFile.cs
Normal file
86
PckStudio.ModelSupport/Format/Internal/PSM/PSMFile.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.ModelSupport.Internal.Format
|
||||
{
|
||||
/*
|
||||
Magic - 3 bytes("psm")
|
||||
Version - 1 byte [u8]
|
||||
Anim - 4 bytes[int32]
|
||||
NumberOfParts - 4 bytes[int32]
|
||||
{
|
||||
part parent - 1 byte (HEAD=0, BODY=1, LEG0=2, LEG1=3, ARM0=4, ARM1=5)
|
||||
Position-X - 4 bytes (float32)
|
||||
Position-Y - 4 bytes (float32)
|
||||
Position-Z - 4 bytes (float32)
|
||||
Size-X - 4 bytes (float32)
|
||||
Size-Y - 4 bytes (float32)
|
||||
Size-Z - 4 bytes (float32)
|
||||
MirrorAndUvX - 1 bit flag 7 bits uv.x value(0-64) (s8)
|
||||
HideWithArmorAndUvY - 1 bit flag 7 bits uv.y value(0-64) (s8)
|
||||
inflation/scale value - 4 bytes (float32)
|
||||
}
|
||||
NumberOfOffsets - 4 bytes[int32]
|
||||
{
|
||||
offset part - 1 byte
|
||||
vertical offset - 4 bytes[float]
|
||||
}
|
||||
*/
|
||||
public sealed class PSMFile
|
||||
{
|
||||
internal static readonly string HEADER_MAGIC = "psm";
|
||||
internal const byte CurrentVersion = 1;
|
||||
|
||||
public readonly byte Version;
|
||||
|
||||
internal PSMFile(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
internal PSMFile(byte version, SkinANIM skinANIM)
|
||||
: this(version)
|
||||
{
|
||||
SkinANIM = skinANIM;
|
||||
}
|
||||
|
||||
public SkinANIM SkinANIM { get; private set; }
|
||||
|
||||
public readonly List<SkinBOX> Parts = new List<SkinBOX>();
|
||||
public readonly List<SkinPartOffset> Offsets = new List<SkinPartOffset>();
|
||||
}
|
||||
|
||||
public enum PSMOffsetType : byte
|
||||
{
|
||||
HEAD = 0,
|
||||
BODY = 1,
|
||||
ARM0 = 2,
|
||||
ARM1 = 3,
|
||||
LEG0 = 4,
|
||||
LEG1 = 5,
|
||||
|
||||
TOOL0 = 6,
|
||||
TOOL1 = 7,
|
||||
|
||||
HELMET = 8,
|
||||
SHOULDER0 = 9,
|
||||
SHOULDER1 = 10,
|
||||
CHEST = 11,
|
||||
WAIST = 12,
|
||||
PANTS0 = 13,
|
||||
PANTS1 = 14,
|
||||
BOOT0 = 15,
|
||||
BOOT1 = 16,
|
||||
}
|
||||
|
||||
public enum PSMParentType : byte
|
||||
{
|
||||
HEAD = 0,
|
||||
BODY = 1,
|
||||
ARM0 = 2,
|
||||
ARM1 = 3,
|
||||
LEG0 = 4,
|
||||
LEG1 = 5,
|
||||
}
|
||||
}
|
||||
156
PckStudio.ModelSupport/Format/Internal/PSM/PSMFileReader.cs
Normal file
156
PckStudio.ModelSupport/Format/Internal/PSM/PSMFileReader.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OMI;
|
||||
using OMI.Workers;
|
||||
using PckStudio.Core.FileFormats;
|
||||
using PckStudio.Core;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.ModelSupport.Internal.Format
|
||||
{
|
||||
internal class PSMFileReader : IDataFormatReader<PSMFile>, IDataFormatReader
|
||||
{
|
||||
public PSMFile FromFile(string filename)
|
||||
{
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
using (var fs = File.OpenRead(filename))
|
||||
{
|
||||
return FromStream(fs);
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException(filename);
|
||||
}
|
||||
|
||||
public PSMFile FromStream(Stream stream)
|
||||
{
|
||||
using var reader = new EndiannessAwareBinaryReader(stream, Encoding.ASCII, leaveOpen: true, ByteOrder.LittleEndian);
|
||||
|
||||
var magic = reader.ReadString(3);
|
||||
if (magic != PSMFile.HEADER_MAGIC)
|
||||
{
|
||||
Trace.TraceError("PSMFileReader.FromStream - Failed to load csmb.\n\tReason: Header magic mismatch.");
|
||||
return new PSMFile(byte.MaxValue);
|
||||
}
|
||||
|
||||
byte version = reader.ReadByte();
|
||||
if (version < 1 || version > 1)
|
||||
{
|
||||
Trace.TraceError("PSMFileReader.FromStream - Failed to load csmb.\n\tReason: Unsupported version.");
|
||||
return new PSMFile(byte.MaxValue);
|
||||
}
|
||||
|
||||
var skinANIM = SkinANIM.FromValue(reader.ReadInt32());
|
||||
PSMFile csmbFile = new PSMFile(version, skinANIM);
|
||||
int numOfParts = reader.ReadInt32();
|
||||
for (int i = 0; i < numOfParts; i++)
|
||||
{
|
||||
SkinBOX part = ReadPart(reader);
|
||||
csmbFile.Parts.Add(part);
|
||||
}
|
||||
int numOfOffsets = reader.ReadInt32();
|
||||
for (int i = 0; i < numOfOffsets; i++)
|
||||
{
|
||||
SkinPartOffset offset = ReadOffset(reader);
|
||||
csmbFile.Offsets.Add(offset);
|
||||
}
|
||||
|
||||
return csmbFile;
|
||||
}
|
||||
|
||||
private SkinBOX ReadPart(EndiannessAwareBinaryReader reader)
|
||||
{
|
||||
string type = GetParentType((PSMParentType)reader.ReadByte());
|
||||
float posX = reader.ReadSingle();
|
||||
float posY = reader.ReadSingle();
|
||||
float posZ = reader.ReadSingle();
|
||||
float sizeX = reader.ReadSingle();
|
||||
float sizeY = reader.ReadSingle();
|
||||
float sizeZ = reader.ReadSingle();
|
||||
byte mirrorAndUvX = reader.ReadByte();
|
||||
byte hideWithArmorAndUvY = reader.ReadByte();
|
||||
int uvX = mirrorAndUvX & 0x7f;
|
||||
int uvY = hideWithArmorAndUvY & 0x7f;
|
||||
bool mirror = (mirrorAndUvX & 0x80) != 0;
|
||||
bool hideWithArmor = (hideWithArmorAndUvY & 0x80) != 0;
|
||||
float scale = reader.ReadSingle();
|
||||
return new SkinBOX(type, new System.Numerics.Vector3(posX, posY, posZ), new System.Numerics.Vector3(sizeX, sizeY, sizeZ), new System.Numerics.Vector2(uvX, uvY), hideWithArmor, mirror, scale);
|
||||
}
|
||||
|
||||
private SkinPartOffset ReadOffset(EndiannessAwareBinaryReader reader)
|
||||
{
|
||||
PSMOffsetType type = (PSMOffsetType)reader.ReadByte();
|
||||
float value = reader.ReadSingle();
|
||||
return new SkinPartOffset(GetOffsetType(type), value);
|
||||
}
|
||||
|
||||
private static string GetParentType(PSMParentType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PSMParentType.HEAD:
|
||||
return "HEAD";
|
||||
case PSMParentType.BODY:
|
||||
return "BODY";
|
||||
case PSMParentType.ARM0:
|
||||
return "ARM0";
|
||||
case PSMParentType.ARM1:
|
||||
return "ARM1";
|
||||
case PSMParentType.LEG0:
|
||||
return "LEG0";
|
||||
case PSMParentType.LEG1:
|
||||
return "LEG1";
|
||||
default:
|
||||
throw new InvalidDataException(type.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetOffsetType(PSMOffsetType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PSMOffsetType.HEAD:
|
||||
return "HEAD";
|
||||
case PSMOffsetType.BODY:
|
||||
return "BODY";
|
||||
case PSMOffsetType.ARM0:
|
||||
return "ARM0";
|
||||
case PSMOffsetType.ARM1:
|
||||
return "ARM1";
|
||||
case PSMOffsetType.LEG0:
|
||||
return "LEG0";
|
||||
case PSMOffsetType.LEG1:
|
||||
return "LEG1";
|
||||
case PSMOffsetType.TOOL0:
|
||||
return "TOOL0";
|
||||
case PSMOffsetType.TOOL1:
|
||||
return "TOOL1";
|
||||
case PSMOffsetType.HELMET:
|
||||
return "HELMET";
|
||||
case PSMOffsetType.SHOULDER0:
|
||||
return "SHOULDER0";
|
||||
case PSMOffsetType.SHOULDER1:
|
||||
return "SHOULDER1";
|
||||
case PSMOffsetType.CHEST:
|
||||
return "CHEST";
|
||||
case PSMOffsetType.WAIST:
|
||||
return "WAIST";
|
||||
case PSMOffsetType.PANTS0:
|
||||
return "PANTS0";
|
||||
case PSMOffsetType.PANTS1:
|
||||
return "PANTS1";
|
||||
case PSMOffsetType.BOOT0:
|
||||
return "BOOT0";
|
||||
case PSMOffsetType.BOOT1:
|
||||
return "BOOT1";
|
||||
default:
|
||||
throw new InvalidDataException(type.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
object IDataFormatReader.FromStream(Stream stream) => FromStream(stream);
|
||||
|
||||
object IDataFormatReader.FromFile(string filename) => FromFile(filename);
|
||||
}
|
||||
}
|
||||
134
PckStudio.ModelSupport/Format/Internal/PSM/PSMFileWriter.cs
Normal file
134
PckStudio.ModelSupport/Format/Internal/PSM/PSMFileWriter.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using PckStudio.Core.FileFormats;
|
||||
using OMI.Workers;
|
||||
using OMI;
|
||||
using System;
|
||||
using OpenTK;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.ModelSupport.Internal.Format
|
||||
{
|
||||
internal class PSMFileWriter : IDataFormatWriter
|
||||
{
|
||||
PSMFile _PSM;
|
||||
|
||||
public PSMFileWriter(PSMFile csmb)
|
||||
{
|
||||
_PSM = csmb;
|
||||
}
|
||||
|
||||
public void WriteToFile(string filename)
|
||||
{
|
||||
using(var fs = File.OpenWrite(filename))
|
||||
{
|
||||
WriteToStream(fs);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteToStream(Stream stream)
|
||||
{
|
||||
using (var writer = new EndiannessAwareBinaryWriter(stream, Encoding.ASCII, leaveOpen: true, ByteOrder.LittleEndian))
|
||||
{
|
||||
writer.WriteString(PSMFile.HEADER_MAGIC);
|
||||
writer.Write(_PSM.Version);
|
||||
writer.Write(_PSM.SkinANIM.ToValue());
|
||||
writer.Write(_PSM.Parts.Count);
|
||||
foreach (SkinBOX part in _PSM.Parts)
|
||||
{
|
||||
WritePart(writer, part);
|
||||
}
|
||||
writer.Write(_PSM.Offsets.Count);
|
||||
foreach (SkinPartOffset offset in _PSM.Offsets)
|
||||
{
|
||||
writer.Write((byte)GetOffsetPart(offset.Type));
|
||||
writer.Write(offset.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePart(EndiannessAwareBinaryWriter writer, SkinBOX part)
|
||||
{
|
||||
writer.Write((byte)GetParentPart(part.Type));
|
||||
writer.Write(part.Pos.X);
|
||||
writer.Write(part.Pos.Y);
|
||||
writer.Write(part.Pos.Z);
|
||||
writer.Write(part.Size.X);
|
||||
writer.Write(part.Size.Y);
|
||||
writer.Write(part.Size.Z);
|
||||
|
||||
byte uvX = (byte)MathHelper.Clamp((int)part.UV.X, 0, 64);
|
||||
byte uvY = (byte)MathHelper.Clamp((int)part.UV.Y, 0, 64);
|
||||
byte mirrorAndUvX = (byte)(Convert.ToByte(part.Mirror) << 7 | uvX);
|
||||
byte hideWithArmorAndUvY = (byte)(Convert.ToByte(part.HideWithArmor) << 7 | uvY);
|
||||
|
||||
writer.Write(mirrorAndUvX);
|
||||
writer.Write(hideWithArmorAndUvY);
|
||||
writer.Write(part.Scale);
|
||||
}
|
||||
|
||||
private static PSMParentType GetParentPart(string type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "HEAD":
|
||||
return PSMParentType.HEAD;
|
||||
case "BODY":
|
||||
return PSMParentType.BODY;
|
||||
case "ARM0":
|
||||
return PSMParentType.ARM0;
|
||||
case "ARM1":
|
||||
return PSMParentType.ARM1;
|
||||
case "LEG0":
|
||||
return PSMParentType.LEG0;
|
||||
case "LEG1":
|
||||
return PSMParentType.LEG1;
|
||||
default:
|
||||
throw new InvalidDataException(type);
|
||||
}
|
||||
}
|
||||
|
||||
private static PSMOffsetType GetOffsetPart(string type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "HEAD":
|
||||
return PSMOffsetType.HEAD;
|
||||
case "BODY":
|
||||
return PSMOffsetType.BODY;
|
||||
case "ARM0":
|
||||
return PSMOffsetType.ARM0;
|
||||
case "ARM1":
|
||||
return PSMOffsetType.ARM1;
|
||||
case "LEG0":
|
||||
return PSMOffsetType.LEG0;
|
||||
case "LEG1":
|
||||
return PSMOffsetType.LEG1;
|
||||
case "TOOL0":
|
||||
return PSMOffsetType.TOOL0;
|
||||
case "TOOL1":
|
||||
return PSMOffsetType.TOOL1;
|
||||
case "HELMET":
|
||||
return PSMOffsetType.HELMET;
|
||||
case "SHOULDER0":
|
||||
return PSMOffsetType.SHOULDER0;
|
||||
case "SHOULDER1":
|
||||
return PSMOffsetType.SHOULDER1;
|
||||
case "CHEST":
|
||||
return PSMOffsetType.CHEST;
|
||||
case "WAIST":
|
||||
return PSMOffsetType.WAIST;
|
||||
case "PANTS0":
|
||||
return PSMOffsetType.PANTS0;
|
||||
case "PANTS1":
|
||||
return PSMOffsetType.PANTS1;
|
||||
case "BOOT0":
|
||||
return PSMOffsetType.BOOT0;
|
||||
case "BOOT1":
|
||||
return PSMOffsetType.BOOT1;
|
||||
default:
|
||||
throw new InvalidDataException(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
239
PckStudio.ModelSupport/GameModelImporter.cs
Normal file
239
PckStudio.ModelSupport/GameModelImporter.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
/* 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.IO;
|
||||
using System.Linq;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using OMI.Formats.Model;
|
||||
|
||||
using PckStudio.Core.Json;
|
||||
using PckStudio.Core.Extensions;
|
||||
using PckStudio.Core;
|
||||
using PckStudio.ModelSupport.Format.External;
|
||||
using PckStuido.ModelSupport.Properties;
|
||||
using NamedTexture = PckStudio.Core.NamedData<System.Drawing.Image>;
|
||||
|
||||
namespace PckStudio.ModelSupport
|
||||
{
|
||||
public sealed class GameModelImporter : ModelImporter<GameModelInfo>
|
||||
{
|
||||
public static GameModelImporter Default { get; } = new GameModelImporter();
|
||||
|
||||
public sealed class ModelExportSettings
|
||||
{
|
||||
public bool CreateModelOutline { get; set; } = true;
|
||||
}
|
||||
|
||||
public ModelExportSettings ExportSettings { get; } = new ModelExportSettings();
|
||||
|
||||
public sealed class ModelImportSettings
|
||||
{
|
||||
public int ModelVersion { get; set; } = 1;
|
||||
}
|
||||
|
||||
public ModelImportSettings ImportSettings { get; } = new ModelImportSettings();
|
||||
|
||||
public static ReadOnlyDictionary<string, JsonModelMetaData> ModelMetaData { get; } = JsonConvert.DeserializeObject<ReadOnlyDictionary<string, JsonModelMetaData>>(Resources.modelMetaData);
|
||||
public static ReadOnlyDictionary<string, DefaultModel> DefaultModels { get; } = JsonConvert.DeserializeObject<ReadOnlyDictionary<string, DefaultModel>>(Resources.defaultModels);
|
||||
|
||||
private GameModelImporter()
|
||||
{
|
||||
// TODO: add import functionality -miku
|
||||
InternalAddProvider(new FileDialogFilter("Block bench model(*.bbmodel)", "*.bbmodel"), ImportBlockBenchModel, ExportBlockBenchModel);
|
||||
}
|
||||
|
||||
private readonly Vector3 bbModelTransformAxis = new Vector3(1, 1, 0);
|
||||
// maybe get this value from the json. -miku
|
||||
private readonly Vector3 _heightOffset = Vector3.UnitY * 24f;
|
||||
|
||||
private void ExportBlockBenchModel(string filepath, GameModelInfo modelInfo)
|
||||
{
|
||||
BlockBenchModel blockBenchModel = BlockBenchModel.Create(BlockBenchFormatInfos.BedrockEntity, modelInfo.Model.Name, modelInfo.Model.TextureSize, modelInfo.Textures.Select(nt => (Texture)nt));
|
||||
blockBenchModel.ModelIdentifier = modelInfo.Model.Name;
|
||||
|
||||
List<Element> elements = new List<Element>(modelInfo.Model.PartCount);
|
||||
|
||||
if (!ModelMetaData.TryGetValue(modelInfo.Model.Name, out JsonModelMetaData modelMetaData))
|
||||
{
|
||||
Trace.TraceError($"[{nameof(GameModelImporter)}:{nameof(ExportBlockBenchModel)}] Failed to get model meta data for '{modelInfo.Model.Name}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<Outline> outlines = ConvertToOutlines(modelInfo.Model, Vector3.Zero, modelMetaData.RootParts, elements.AddRange);
|
||||
|
||||
blockBenchModel.Elements = elements.ToArray();
|
||||
if (ExportSettings.CreateModelOutline)
|
||||
outlines = new Outline[1]
|
||||
{
|
||||
new Outline(modelInfo.Model.Name) { Children = JArray.FromObject(outlines) }
|
||||
};
|
||||
|
||||
blockBenchModel.Outliner = JArray.FromObject(outlines);
|
||||
|
||||
string content = JsonConvert.SerializeObject(blockBenchModel, Formatting.Indented);
|
||||
File.WriteAllText(filepath, content);
|
||||
}
|
||||
|
||||
private Element ToElement(string partName, ModelBox modelBox, Vector3 partTranslation)
|
||||
{
|
||||
Element element = CreateElement(partName, modelBox, partTranslation, bbModelTransformAxis, _heightOffset);
|
||||
//element.Rotation = rotation * TransformSpace(Vector3.One, Vector3.Zero, bbModelTransformAxis);
|
||||
//element.Origin = outline.Origin;
|
||||
return element;
|
||||
}
|
||||
|
||||
private Outline[] ConvertToOutlines(Model model, Vector3 parentRotation, IReadOnlyCollection<ModelMetaDataPart> keyValues, Action<Element[]> addElements, int depth = 0)
|
||||
{
|
||||
Outline CreateOutline(ModelPart modelPart)
|
||||
{
|
||||
Outline outline = new Outline(modelPart.Name);
|
||||
|
||||
Vector3 partTranslation = modelPart.Translation;
|
||||
outline.Origin = TransformSpace(partTranslation, Vector3.Zero, bbModelTransformAxis);
|
||||
outline.Origin += _heightOffset;
|
||||
|
||||
Vector3 rotation = modelPart.Rotation;
|
||||
outline.Rotation = rotation * TransformSpace(Vector3.One, Vector3.Zero, bbModelTransformAxis);
|
||||
outline.Rotation += parentRotation;
|
||||
|
||||
Element[] elements1 = modelPart.GetBoxes().Select(box => ToElement(modelPart.Name, box, partTranslation)).ToArray();
|
||||
addElements(elements1);
|
||||
|
||||
outline.Children.Add(elements1.Select(element => element.Uuid).ToArray());
|
||||
return outline;
|
||||
}
|
||||
|
||||
if (depth == 0 && keyValues.Count == 0)
|
||||
{
|
||||
return model.GetParts().Select(CreateOutline).ToArray();
|
||||
}
|
||||
|
||||
List<Outline> outlines = new List<Outline>();
|
||||
foreach (ModelMetaDataPart item in keyValues)
|
||||
{
|
||||
if (!model.TryGetPart(item.Name, out ModelPart modelPart))
|
||||
{
|
||||
Debug.WriteLine($"{nameof(item.Name)}: '{item.Name}' not in {nameof(model)}.");
|
||||
continue;
|
||||
}
|
||||
Outline partentOutline = CreateOutline(modelPart);
|
||||
JToken[] s = ConvertToOutlines(model, modelPart.Rotation, item.Children, addElements, depth + 1).Select(JToken.FromObject).ToArray();
|
||||
partentOutline.Children.Add(s);
|
||||
outlines.Add(partentOutline);
|
||||
}
|
||||
return outlines.ToArray();
|
||||
}
|
||||
|
||||
|
||||
private static Element CreateElement(string name, ModelBox box, Vector3 origin, Vector3 translationUnit, Vector3 offset)
|
||||
{
|
||||
Vector3 pos = box.Position;
|
||||
Vector3 size = box.Size;
|
||||
Vector3 transformPos = TransformSpace(pos + origin, size, translationUnit) + offset;
|
||||
return Element.CreateCube(name, box.Uv, transformPos, size, box.Inflate, box.Mirror);
|
||||
}
|
||||
|
||||
private GameModelInfo ImportBlockBenchModel(string filepath)
|
||||
{
|
||||
BlockBenchModel blockBenchModel = JsonConvert.DeserializeObject<BlockBenchModel>(File.ReadAllText(filepath));
|
||||
if (!blockBenchModel.Format.UseBoxUv)
|
||||
{
|
||||
Trace.TraceError($"[{nameof(GameModelImporter)}:{nameof(ImportBlockBenchModel)}] Failed to import model '{blockBenchModel.ModelIdentifier}': Model does not use box uv.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ModelMetaData.TryGetValue(blockBenchModel.ModelIdentifier, out JsonModelMetaData modelMetaData))
|
||||
{
|
||||
Trace.TraceError($"[{nameof(GameModelImporter)}:{nameof(ImportBlockBenchModel)}] Failed to import model '{blockBenchModel.ModelIdentifier}': No model meta data found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<NamedTexture> textures = blockBenchModel.Textures
|
||||
.Where(t => modelMetaData.TextureLocations.Any(texName => !string.IsNullOrEmpty(t.Name) && texName.EndsWith(Path.GetFileNameWithoutExtension(t.Name))))
|
||||
.Select(t => new NamedTexture(modelMetaData.TextureLocations.First(texName => texName.EndsWith(Path.GetFileNameWithoutExtension(t.Name))), (Image)t));
|
||||
|
||||
Model model = new Model(blockBenchModel.ModelIdentifier, blockBenchModel.TextureResolution);
|
||||
|
||||
JArray rootOutline = blockBenchModel.Outliner
|
||||
.FirstOrDefault(token => token.Type == JTokenType.Object && token.ToObject<Outline>().Name == blockBenchModel.ModelIdentifier)
|
||||
?.ToObject<Outline>().Children ?? blockBenchModel.Outliner;
|
||||
|
||||
foreach (Outline outline in rootOutline.Where(token => token.Type == JTokenType.Object).Select(token => token.ToObject<Outline>()))
|
||||
{
|
||||
foreach (ModelPart part in ConvertOutlineToModelPart(outline, blockBenchModel.Elements))
|
||||
{
|
||||
model.AddPart(part);
|
||||
}
|
||||
}
|
||||
|
||||
return new GameModelInfo(model, textures);
|
||||
}
|
||||
|
||||
private IEnumerable<ModelPart> ConvertOutlineToModelPart(Outline root, IReadOnlyCollection<Element> elements)
|
||||
{
|
||||
List<ModelPart> parts = new List<ModelPart>(
|
||||
root.Children
|
||||
.Where(token => token.Type == JTokenType.Object)
|
||||
.SelectMany(token => ConvertOutlineToModelPart(token.ToObject<Outline>(), elements))
|
||||
);
|
||||
|
||||
IEnumerable<Element> modelBoxElements = root.Children
|
||||
.Where(token => token.Type == JTokenType.String && Guid.TryParse(token.ToString(), out Guid _))
|
||||
.Select(token => elements.First(e => e.Uuid == Guid.Parse(token.ToString())))
|
||||
.Where(element => element.Type == "cube" && element.UseBoxUv && element.Export);
|
||||
|
||||
Vector3 additionalRotation = new Vector3();
|
||||
Element first = modelBoxElements.FirstOrDefault() ?? new Element() { Rotation = Vector3.Zero };
|
||||
if (first.Rotation != Vector3.Zero)
|
||||
{
|
||||
if (!modelBoxElements.All(e => e.Rotation == first.Rotation))
|
||||
{
|
||||
Trace.TraceError($"[{nameof(GameModelImporter)}:{nameof(ImportBlockBenchModel)}] Rotation can't be applied for single elements.");
|
||||
return Enumerable.Empty<ModelPart>();
|
||||
}
|
||||
additionalRotation = first.Rotation;
|
||||
}
|
||||
Vector3 translation = TransformSpace(root.Origin - _heightOffset, Vector3.Zero, bbModelTransformAxis);
|
||||
Vector3 rotation = TransformSpace(root.Rotation, Vector3.Zero, bbModelTransformAxis);
|
||||
ModelPart part = new ModelPart(root.Name, string.Empty, translation, rotation, additionalRotation);
|
||||
part.AddBoxes(modelBoxElements.Select(box => ConvertElementToModelBox(box, part.Translation)));
|
||||
parts.Add(part);
|
||||
return parts;
|
||||
}
|
||||
|
||||
private ModelBox ConvertElementToModelBox(Element element, Vector3 translation)
|
||||
{
|
||||
BoundingBox boundingBox = new BoundingBox(element.From, element.To);
|
||||
|
||||
Vector3 pos = boundingBox.Start.ToNumericsVector();
|
||||
Vector3 size = boundingBox.Volume.ToNumericsVector();
|
||||
|
||||
Vector3 transformedPos = TransformSpace(pos, size, bbModelTransformAxis) - translation + _heightOffset;
|
||||
|
||||
return new ModelBox(transformedPos, size, element.UvOffset, element.Inflate, element.MirrorUv);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
PckStudio.ModelSupport/GameModelInfo.cs
Normal file
20
PckStudio.ModelSupport/GameModelInfo.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using OMI.Formats.Model;
|
||||
using NamedTexture = PckStudio.Core.NamedData<System.Drawing.Image>;
|
||||
|
||||
namespace PckStudio.ModelSupport
|
||||
{
|
||||
public sealed class GameModelInfo
|
||||
{
|
||||
public Model Model { get; }
|
||||
|
||||
public IEnumerable<NamedTexture> Textures { get; }
|
||||
|
||||
public GameModelInfo(Model model, IEnumerable<NamedTexture> textures)
|
||||
{
|
||||
Model = model;
|
||||
Textures = textures;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
48
PckStudio.ModelSupport/Json/JsonDefaultModel.cs
Normal file
48
PckStudio.ModelSupport/Json/JsonDefaultModel.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PckStudio.ModelSupport
|
||||
{
|
||||
public class DefaultModel
|
||||
{
|
||||
[JsonProperty("textureSize", Required = Required.Always)]
|
||||
public Vector2 TextureSize { get; set; }
|
||||
|
||||
[JsonProperty("parts", Required = Required.Always)]
|
||||
public DefaultPart[] Parts { get; set; } = Array.Empty<DefaultPart>();
|
||||
}
|
||||
|
||||
public class DefaultPart
|
||||
{
|
||||
[JsonProperty("name", Required = Required.Always)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("translation")]
|
||||
public Vector3 Translation { get; set; } = Vector3.Zero;
|
||||
|
||||
[JsonProperty("rotation")]
|
||||
public Vector3 Rotation { get; set; } = Vector3.Zero;
|
||||
|
||||
[JsonProperty("boxes")]
|
||||
public ModelDefaultBox[] Boxes { get; set; }
|
||||
}
|
||||
|
||||
public class ModelDefaultBox
|
||||
{
|
||||
[JsonProperty("pos")]
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
[JsonProperty("size")]
|
||||
public Vector3 Size { get; set; }
|
||||
|
||||
[JsonProperty("uv")]
|
||||
public Vector2 Uv { get; set; }
|
||||
|
||||
[JsonProperty("mirror")]
|
||||
public bool Mirror { get; set; } = false;
|
||||
|
||||
[JsonProperty("inflate")]
|
||||
public float Inflate { get; set; } = 0f;
|
||||
}
|
||||
}
|
||||
46
PckStudio.ModelSupport/Json/JsonModelMetaData.cs
Normal file
46
PckStudio.ModelSupport/Json/JsonModelMetaData.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PckStudio.ModelSupport
|
||||
{
|
||||
public class ModelMetaDataPart
|
||||
{
|
||||
[JsonProperty("name", Required = Required.Always)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("children", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public ModelMetaDataPart[] Children { get; set; } = Array.Empty<ModelMetaDataPart>();
|
||||
|
||||
[JsonConstructor]
|
||||
public ModelMetaDataPart()
|
||||
{
|
||||
}
|
||||
|
||||
public ModelMetaDataPart(string name)
|
||||
: this(name, Array.Empty<ModelMetaDataPart>())
|
||||
{
|
||||
}
|
||||
|
||||
public ModelMetaDataPart(string name, params ModelMetaDataPart[] children)
|
||||
{
|
||||
Name = name;
|
||||
Children = children;
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonModelMetaData
|
||||
{
|
||||
[JsonProperty("textureLocations", Required = Required.Always)]
|
||||
public string[] TextureLocations { get; set; }
|
||||
|
||||
[JsonProperty("materialName", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("uv_offsets", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Vector2[] UvOffsets { get; set; } = Array.Empty<Vector2>();
|
||||
|
||||
[JsonProperty("parts", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public ModelMetaDataPart[] RootParts { get; set; } = Array.Empty<ModelMetaDataPart>();
|
||||
}
|
||||
}
|
||||
178
PckStudio.ModelSupport/ModelImporter.cs
Normal file
178
PckStudio.ModelSupport/ModelImporter.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
/* 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<T> where T : class
|
||||
{
|
||||
private Dictionary<string, IModelImportProvider<T>> _importProviders = new Dictionary<string, IModelImportProvider<T>>();
|
||||
|
||||
private sealed class InternalImportProvider : IModelImportProvider<T>
|
||||
{
|
||||
public string Name => nameof(InternalImportProvider);
|
||||
|
||||
public FileDialogFilter DialogFilter => _dialogFilter;
|
||||
|
||||
public bool SupportImport => _import != null;
|
||||
|
||||
public bool SupportExport => _export != null;
|
||||
|
||||
private FileDialogFilter _dialogFilter;
|
||||
private Func<string, T> _import;
|
||||
private Action<string, T> _export;
|
||||
|
||||
public InternalImportProvider(FileDialogFilter dialogFilter, Func<string, T> import, Action<string, T> 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter that can be used for <see cref="System.Windows.Forms.OpenFileDialog"/> or <see cref="System.Windows.Forms.SaveFileDialog"/>
|
||||
/// </summary>
|
||||
public string SupportedModelFileFormatsFilter => string.Join("|", _importProviders.Values.Select(p => p.DialogFilter));
|
||||
|
||||
public T Import(string filename)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Trace.TraceWarning($"[{nameof(ModelImporter<T>)}:Import] Failed to import '{filename}'. File does not exist.");
|
||||
return default;
|
||||
}
|
||||
|
||||
if (!HasProvider(filename))
|
||||
{
|
||||
Trace.TraceWarning($"[{nameof(ModelImporter<T>)}:Import] No provider found for '{Path.GetExtension(filename)}'.");
|
||||
return default;
|
||||
}
|
||||
|
||||
IModelImportProvider<T> 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<T>)}:Export] Model is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasProvider(filename))
|
||||
{
|
||||
Trace.TraceWarning($"[{nameof(ModelImporter<T>)}:Export] No provider found for '{Path.GetExtension(filename)}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
IModelImportProvider<T> provider = GetProvider(filename);
|
||||
if (!provider.SupportExport)
|
||||
{
|
||||
throw new NotSupportedException($"Provider '{provider.Name}' does not support exporting.");
|
||||
}
|
||||
provider.Export(filename, model);
|
||||
}
|
||||
|
||||
internal bool AddProvider(IModelImportProvider<T> provider)
|
||||
{
|
||||
if (_importProviders.ContainsKey(provider.DialogFilter.Extension))
|
||||
return false;
|
||||
|
||||
_importProviders.Add(provider.DialogFilter.Extension, provider);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool InternalAddProvider(FileDialogFilter dialogFilter, Func<string, T> import, Action<string, T> export)
|
||||
{
|
||||
return AddProvider(new InternalImportProvider(dialogFilter, import, export));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates coordinate unit system into our coordinate system
|
||||
/// </summary>
|
||||
/// <param name="origin">Position/Origin of the Object(Cube).</param>
|
||||
/// <param name="size">The Size of the Object(Cube).</param>
|
||||
/// <param name="translationUnit">Describes what axises need translation.</param>
|
||||
/// <returns>The translated position</returns>
|
||||
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<T> GetProvider(string filename)
|
||||
{
|
||||
string fileExtension = Path.GetExtension(filename);
|
||||
return _importProviders.ContainsKey(fileExtension) ? _importProviders[fileExtension] : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
96
PckStudio.ModelSupport/PckStudio.ModelSupport.csproj
Normal file
96
PckStudio.ModelSupport/PckStudio.ModelSupport.csproj
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{43BCACD7-5405-4499-9B45-E1435AC03C26}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<DefineConstants Condition="'$(Configuration)' != 'Debug'">NDEBUG</DefineConstants>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>PckStuido.ModelSupport</RootNamespace>
|
||||
<AssemblyName>PckStuido.ModelSupport</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Extension\SkinExtension.cs" />
|
||||
<Compile Include="Format\External\BedrockLegacyModel.cs" />
|
||||
<Compile Include="Format\External\BedrockModel.cs" />
|
||||
<Compile Include="Format\External\BlockBenchModel.cs" />
|
||||
<Compile Include="Format\Internal\PSM\PSMFile.cs" />
|
||||
<Compile Include="Format\Internal\PSM\PSMFileReader.cs" />
|
||||
<Compile Include="Format\Internal\PSM\PSMFileWriter.cs" />
|
||||
<Compile Include="GameModelImporter.cs" />
|
||||
<Compile Include="GameModelInfo.cs" />
|
||||
<Compile Include="Json\JsonDefaultModel.cs" />
|
||||
<Compile Include="Json\JsonModelMetaData.cs" />
|
||||
<Compile Include="ModelImporter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SkinModelImporter.cs" />
|
||||
<Compile Include="SkinModelInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>13.0.3</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PckStudio.Core\PckStudio.Core.csproj">
|
||||
<Project>{345eabed-f0d1-4d04-b409-babdef747352}</Project>
|
||||
<Name>PckStudio.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Vendor\OMI-Lib\OMI Filetypes Library\OMI Filetype Library.csproj">
|
||||
<Project>{693aebc1-293d-4df0-bcae-26a1099fe7bb}</Project>
|
||||
<Name>OMI Filetype Library</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\defaultModels.json" />
|
||||
<None Include="Resources\modelMetaData.json" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
33
PckStudio.ModelSupport/Properties/AssemblyInfo.cs
Normal file
33
PckStudio.ModelSupport/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("PckStuido.ModelSupport")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("PckStuido.ModelSupport")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("43bcacd7-5405-4499-9b45-e1435ac03c26")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
120
PckStudio.ModelSupport/Properties/Resources.Designer.cs
generated
Normal file
120
PckStudio.ModelSupport/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,120 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PckStuido.ModelSupport.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PckStuido.ModelSupport.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {
|
||||
/// "bat": {
|
||||
/// "textureSize": { "X": 64, "Y": 64 },
|
||||
/// "parts": [
|
||||
/// {
|
||||
/// "name": "head",
|
||||
/// "boxes": [
|
||||
/// { "pos": { "X": -3, "Y": -3, "Z": -3 }, "size": { "X": 6, "Y": 6, "Z": 6 }, "uv": { "X": 0, "Y": 0 } }
|
||||
/// ]
|
||||
/// },
|
||||
/// {
|
||||
/// "name": "body",
|
||||
/// "boxes": [
|
||||
/// { "pos": { "X": -3, "Y": 4, "Z": -3 }, "size": { "X": 6, "Y": 12, "Z": 6 }, "uv": { "X": 0, "Y": 16 } },
|
||||
/// { "pos": { "X": -5, "Y": 16, "Z": 0 }, "size": { "X": 10, "Y": 6, "Z": [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string defaultModels {
|
||||
get {
|
||||
return ResourceManager.GetString("defaultModels", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {
|
||||
/// "bat": {
|
||||
/// "textureLocations": [
|
||||
/// "res/mob/bat"
|
||||
/// ],
|
||||
/// "materialName": "bat",
|
||||
/// "parts": [
|
||||
/// {
|
||||
/// "name": "head",
|
||||
/// "children": [
|
||||
/// { "name": "rightEar" },
|
||||
/// { "name": "leftEar" }
|
||||
/// ]
|
||||
/// },
|
||||
/// {
|
||||
/// "name": "body",
|
||||
/// "children": [
|
||||
/// {
|
||||
/// "name": "rightWing",
|
||||
/// "children": [
|
||||
/// { "name": "rightWingTip" }
|
||||
/// ]
|
||||
/// },
|
||||
/// {
|
||||
/// "name": "leftWing",
|
||||
/// [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string modelMetaData {
|
||||
get {
|
||||
return ResourceManager.GetString("modelMetaData", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
127
PckStudio.ModelSupport/Properties/Resources.resx
Normal file
127
PckStudio.ModelSupport/Properties/Resources.resx
Normal file
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="defaultModels" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\defaultModels.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
|
||||
</data>
|
||||
<data name="modelMetaData" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\modelMetaData.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;iso-8859-1</value>
|
||||
</data>
|
||||
</root>
|
||||
683
PckStudio.ModelSupport/Resources/defaultModels.json
Normal file
683
PckStudio.ModelSupport/Resources/defaultModels.json
Normal file
@@ -0,0 +1,683 @@
|
||||
{
|
||||
"bat": {
|
||||
"textureSize": { "X": 64, "Y": 64 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -3, "Y": -3, "Z": -3 }, "size": { "X": 6, "Y": 6, "Z": 6 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -3, "Y": 4, "Z": -3 }, "size": { "X": 6, "Y": 12, "Z": 6 }, "uv": { "X": 0, "Y": 16 } },
|
||||
{ "pos": { "X": -5, "Y": 16, "Z": 0 }, "size": { "X": 10, "Y": 6, "Z": 1 }, "uv": { "X": 0, "Y": 34 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightEar",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -6, "Z": -2 }, "size": { "X": 3, "Y": 4, "Z": 1 }, "uv": { "X": 24, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftEar",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 1, "Y": -6, "Z": -2 }, "size": { "X": 3, "Y": 4, "Z": 1 }, "uv": { "X": 24, "Y": 0 }, "mirror": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightWing",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -12, "Y": 1, "Z": 1.5 }, "size": { "X": 10, "Y": 16, "Z": 1 }, "uv": { "X": 42, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightWingTip",
|
||||
"translation": { "X": -12, "Y": 1, "Z": 1.5 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -8, "Y": 1, "Z": 0 }, "size": { "X": 8, "Y": 12, "Z": 1 }, "uv": { "X": 24, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftWing",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 2, "Y": 1, "Z": 1.5 }, "size": { "X": 10, "Y": 16, "Z": 1 }, "uv": { "X": 42, "Y": 0 }, "mirror": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftWingTip",
|
||||
"translation": { "X": 12, "Y": 1, "Z": 1.5 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": 1, "Z": 0 }, "size": { "X": 8, "Y": 12, "Z": 1 }, "uv": { "X": 24, "Y": 16 }, "mirror": true }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trident": {
|
||||
"textureSize": { "X": 32, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "pole",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -0.5, "Y": -4, "Z": -0.5 }, "size": { "X": 1, "Y": 31, "Z": 1 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": -1.5, "Y": 0, "Z": -0.5 }, "size": { "X": 3, "Y": 2, "Z": 1 }, "uv": { "X": 4, "Y": 0 } },
|
||||
{ "pos": { "X": -2.5, "Y": -3, "Z": -0.5 }, "size": { "X": 1, "Y": 4, "Z": 1 }, "uv": { "X": 4, "Y": 3 } },
|
||||
{ "pos": { "X": 1.5, "Y": -3, "Z": -0.5 }, "size": { "X": 1, "Y": 4, "Z": 1 }, "uv": { "X": 4, "Y": 3 }, "mirror": true }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"irongolem": {
|
||||
"textureSize": { "X": 128, "Y": 128 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 0, "Z": -2 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -12, "Z": -5.5 }, "size": { "X": 8, "Y": 10, "Z": 8 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": -1, "Y": -5, "Z": -7.5 }, "size": { "X": 2, "Y": 4, "Z": 2 }, "uv": { "X": 24, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -9, "Y": -2, "Z": -6 }, "size": { "X": 18, "Y": 12, "Z": 11 }, "uv": { "X": 0, "Y": 40 } },
|
||||
{ "pos": { "X": -4.5, "Y": 10, "Z": -3 }, "size": { "X": 9, "Y": 5, "Z": 6 }, "uv": { "X": 0, "Y": 70 }, "inflate": 0.5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "arm0",
|
||||
"translation": { "X": 0, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -13, "Y": -2.5, "Z": -3 }, "size": { "X": 4, "Y": 30, "Z": 6 }, "uv": { "X": 60, "Y": 58 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "arm1",
|
||||
"translation": { "X": 0, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": 9, "Y": -2.5, "Z": -3 }, "size": { "X": 4, "Y": 30, "Z": 6 }, "uv": { "X": 60, "Y": 21 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"translation": { "X": -4, "Y": 18, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -3.5, "Y": -3, "Z": -3 }, "size": { "X": 6, "Y": 16, "Z": 5 }, "uv": { "X": 37, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"translation": { "X": 5, "Y": 18, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -3.5, "Y": -3, "Z": -3 }, "size": { "X": 6, "Y": 16, "Z": 5 }, "uv": { "X": 60, "Y": 0 }, "mirror": true }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dolphin": {
|
||||
"textureSize": { "X": 64, "Y": 64 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -7, "Z": -6 }, "size": { "X": 8, "Y": 7, "Z": 6 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "nose",
|
||||
"translation": { "X": 0, "Y": 0, "Z": -10 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": -2, "Z": 0 }, "size": { "X": 2, "Y": 2, "Z": 4 }, "uv": { "X": 0, "Y": 13 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"translation": { "X": 0, "Y": 0, "Z": 0 },
|
||||
"rotation": { "X": 0, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -7, "Z": 0 }, "size": { "X": 8, "Y": 7, "Z": 13 }, "uv": { "X": 0, "Y": 13 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "back_fin",
|
||||
"translation": { "X": 0, "Y": -7, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -0.5, "Y": -5, "Z": -1 }, "size": { "X": 1, "Y": 5, "Z": 4 }, "uv": { "X": 29, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_fin",
|
||||
"translation": { "X": 3, "Y": -1, "Z": 2 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": -1, "Z": -1 }, "size": { "X": 8, "Y": 1, "Z": 4 }, "uv": { "X": 40, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_fin",
|
||||
"translation": { "X": -3, "Y": -1, "Z": 2 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -8, "Y": -1, "Z": -1 }, "size": { "X": 8, "Y": 1, "Z": 4 }, "uv": { "X": 40, "Y": 6 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tail",
|
||||
"translation": { "X": 0, "Y": -2.5, "Z": 14 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": -2.5, "Z": -1 }, "size": { "X": 4, "Y": 5, "Z": 11 }, "uv": { "X": 0, "Y": 33 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tail_fin",
|
||||
"translation": { "X": 0, "Y": 0, "Z": 24 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -5, "Y": -2.5, "Z": -1 }, "size": { "X": 10, "Y": 1, "Z": 6 }, "uv": { "X": 0, "Y": 49 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"creeper_head": {
|
||||
"textureSize": { "X": 64, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -8, "Z": -4 }, "size": { "X": 8, "Y": 8, "Z": 8 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"creeper": {
|
||||
"textureSize": { "X": 64, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 6, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -8, "Z": -4 }, "size": { "X": 8, "Y": 8, "Z": 8 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"translation": { "X": 0, "Y": 6, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": 0, "Z": -2 }, "size": { "X": 8, "Y": 12, "Z": 4 }, "uv": { "X": 16, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"translation": { "X": -2, "Y": 18, "Z": 4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"translation": { "X": 2, "Y": 18, "Z": 4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg2",
|
||||
"translation": { "X": -2, "Y": 18, "Z": -4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg3",
|
||||
"translation": { "X": 2, "Y": 18, "Z": -4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"boat": {
|
||||
"textureSize": { "X": 128, "Y": 64 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "bottom",
|
||||
"translation": { "X": 0, "Y": 3, "Z": 1 },
|
||||
"rotation": { "X": 90, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -14, "Y": -9, "Z": -3 }, "size": { "X": 28, "Y": 16, "Z": 3 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "front",
|
||||
"translation": { "X": 15, "Y": 4, "Z": 0 },
|
||||
"rotation": { "X": 0, "Y": 90, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -8, "Y": -7, "Z": -1 }, "size": { "X": 16, "Y": 6, "Z": 2 }, "uv": { "X": 0, "Y": 27 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "back",
|
||||
"translation": { "X": -15, "Y": 4, "Z": 4 },
|
||||
"rotation": { "X": 0, "Y": 270, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -13, "Y": -7, "Z": -1 }, "size": { "X": 18, "Y": 6, "Z": 2 }, "uv": { "X": 0, "Y": 19 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left",
|
||||
"translation": { "X": 0, "Y": 4, "Z": 9 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -14, "Y": -7, "Z": -1 }, "size": { "X": 28, "Y": 6, "Z": 2 }, "uv": { "X": 0, "Y": 43 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right",
|
||||
"translation": { "X": 0, "Y": 4, "Z": -9 },
|
||||
"rotation": { "X": 0, "Y": 180, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -14, "Y": -7, "Z": -1 }, "size": { "X": 28, "Y": 6, "Z": 2 }, "uv": { "X": 0, "Y": 35 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "paddle_left",
|
||||
"translation": { "X": 3, "Y": -5, "Z": 9 },
|
||||
"rotation": { "X": 0, "Y": 0, "Z": 11.25 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -5 }, "size": { "X": 2, "Y": 2, "Z": 18 }, "uv": { "X": 62, "Y": 0 } },
|
||||
{ "pos": { "X": -1.001, "Y": -3, "Z": 8 }, "size": { "X": 1, "Y": 6, "Z": 7 }, "uv": { "X": 62, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "paddle_right",
|
||||
"translation": { "X": 3, "Y": -5, "Z": -9 },
|
||||
"rotation": { "X": 0, "Y": 180, "Z": 11.25 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -5 }, "size": { "X": 2, "Y": 2, "Z": 18 }, "uv": { "X": 62, "Y": 20 } },
|
||||
{ "pos": { "X": 0.001, "Y": -3, "Z": 8 }, "size": { "X": 1, "Y": 6, "Z": 7 }, "uv": { "X": 62, "Y": 20 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bed":{
|
||||
"textureSize": { "X": 64, "Y": 128 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "bed",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": 0, "Z": 0 }, "size": { "X": 16, "Y": 32, "Z": 6 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": 3, "Y": 31, "Z": 6 }, "size": { "X": 10, "Y": 1, "Z": 3 }, "uv": { "X": 38, "Y": 2 } },
|
||||
{ "pos": { "X": 3, "Y": 0, "Z": 6 }, "size": { "X": 10, "Y": 1, "Z": 3 }, "uv": { "X": 38, "Y": 38 } },
|
||||
{ "pos": { "X": 15, "Y": 3, "Z": 6 }, "size": { "X": 1, "Y": 26, "Z": 3 }, "uv": { "X": 52, "Y": 6 } },
|
||||
{ "pos": { "X": 0, "Y": 3, "Z": 6 }, "size": { "X": 1, "Y": 26, "Z": 3 }, "uv": { "X": 44, "Y": 6 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": 29, "Z": 6 }, "size": { "X": 3, "Y": 3, "Z": 3 }, "uv": { "X": 0, "Y": 44 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 13, "Y": 29, "Z": 6 }, "size": { "X": 3, "Y": 3, "Z": 3 }, "uv": { "X": 12, "Y": 44 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg2",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": 0, "Z": 6 }, "size": { "X": 3, "Y": 3, "Z": 3 }, "uv": { "X": 0, "Y": 38 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg3",
|
||||
"boxes": [
|
||||
{ "pos": { "X": 13, "Y": 0, "Z": 6 }, "size": { "X": 3, "Y": 3, "Z": 3 }, "uv": { "X": 12, "Y": 38 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"chicken": {
|
||||
"textureSize": { "X": 64, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 15, "Z": -4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": -6, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 3 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "beak",
|
||||
"translation": { "X": 0, "Y": 15, "Z": -4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": -4, "Z": -4 }, "size": { "X": 4, "Y": 2, "Z": 2 }, "uv": { "X": 14, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "comb",
|
||||
"translation": { "X": 0, "Y": 15, "Z": -4 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": -2, "Z": -3 }, "size": { "X": 2, "Y": 2, "Z": 2 }, "uv": { "X": 14, "Y": 4 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"translation": { "X": 0, "Y": 16, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -3, "Y": -4, "Z": -3 }, "size": { "X": 6, "Y": 8, "Z": 6 }, "uv": { "X": 0, "Y": 9 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"translation": { "X": -2, "Y": 19, "Z": 1 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -3 }, "size": { "X": 3, "Y": 5, "Z": 3 }, "uv": { "X": 26, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"translation": { "X": 1, "Y": 19, "Z": 1 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -3 }, "size": { "X": 3, "Y": 5, "Z": 3 }, "uv": { "X": 26, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "wing0",
|
||||
"translation": { "X": -4, "Y": 13, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": 0, "Z": -3 }, "size": { "X": 1, "Y": 4, "Z": 6 }, "uv": { "X": 24, "Y": 13 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "wing1",
|
||||
"translation": { "X": 4, "Y": 13, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -3 }, "size": { "X": 1, "Y": 4, "Z": 6 }, "uv": { "X": 24, "Y": 13 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"cow": {
|
||||
"textureSize": { "X": 64, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 4, "Z": -8 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -4, "Z": -6 }, "size": { "X": 8, "Y": 8, "Z": 6 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": -5, "Y": -5, "Z": -4 }, "size": { "X": 1, "Y": 3, "Z": 1 }, "uv": { "X": 22, "Y": 0 } },
|
||||
{ "pos": { "X": 4, "Y": -5, "Z": -4 }, "size": { "X": 1, "Y": 3, "Z": 1 }, "uv": { "X": 22, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"translation": { "X": 0, "Y": 5, "Z": 2 },
|
||||
"rotation": { "X": 90, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -6, "Y": -10, "Z": -7 }, "size": { "X": 12, "Y": 18, "Z": 10 }, "uv": { "X": 18, "Y": 4 } },
|
||||
{ "pos": { "X": -2, "Y": 2, "Z": -8 }, "size": { "X": 4, "Y": 6, "Z": 1 }, "uv": { "X": 52, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"translation": { "X": -4, "Y": 12, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"translation": { "X": 4, "Y": 12, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg2",
|
||||
"translation": { "X": -4, "Y": 12, "Z": -6 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg3",
|
||||
"translation": { "X": 4, "Y": 12, "Z": -6 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"mooshroom": {
|
||||
"textureSize": { "X": 64, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 4, "Z": -8 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -4, "Z": -6 }, "size": { "X": 8, "Y": 8, "Z": 6 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": -5, "Y": -5, "Z": -4 }, "size": { "X": 1, "Y": 3, "Z": 1 }, "uv": { "X": 22, "Y": 0 } },
|
||||
{ "pos": { "X": 4, "Y": -5, "Z": -4 }, "size": { "X": 1, "Y": 3, "Z": 1 }, "uv": { "X": 22, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"translation": { "X": 0, "Y": 5, "Z": 2 },
|
||||
"rotation": { "X": 90, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -6, "Y": -10, "Z": -7 }, "size": { "X": 12, "Y": 18, "Z": 10 }, "uv": { "X": 18, "Y": 4 } },
|
||||
{ "pos": { "X": -2, "Y": 2, "Z": -8 }, "size": { "X": 4, "Y": 6, "Z": 1 }, "uv": { "X": 52, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"translation": { "X": -4, "Y": 12, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"translation": { "X": 4, "Y": 12, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg2",
|
||||
"translation": { "X": -4, "Y": 12, "Z": -6 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg3",
|
||||
"translation": { "X": 4, "Y": 12, "Z": -6 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 12, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dragon_head": {
|
||||
"textureSize": { "X": 256, "Y": 256 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"boxes": [
|
||||
// upperlip
|
||||
{ "pos": { "X": -6, "Y": -1, "Z": -24 }, "size": { "X": 12, "Y": 5, "Z": 16 }, "uv": { "X": 176, "Y": 44 } },
|
||||
// upperhead
|
||||
{ "pos": { "X": -8, "Y": -8, "Z": -10 }, "size": { "X": 16, "Y": 16, "Z": 16 }, "uv": { "X": 112, "Y": 30 } },
|
||||
// scale
|
||||
{ "pos": { "X": 3, "Y": -12, "Z": -4 }, "size": { "X": 2, "Y": 4, "Z": 6 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": -5, "Y": -12, "Z": -4 }, "size": { "X": 2, "Y": 4, "Z": 6 }, "uv": { "X": 0, "Y": 0 }, "mirror": true },
|
||||
// nostril
|
||||
{ "pos": { "X": 3, "Y": -3, "Z": -22 }, "size": { "X": 2, "Y": 2, "Z": 4 }, "uv": { "X": 112, "Y": 0 } },
|
||||
{ "pos": { "X": -5, "Y": -3, "Z": -22 }, "size": { "X": 2, "Y": 2, "Z": 4 }, "uv": { "X": 112, "Y": 0 }, "mirror": true },
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "jaw",
|
||||
"translation": { "X": 0, "Y": 4, "Z": -8 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -6, "Y": 0, "Z": -16 }, "size": { "X": 12, "Y": 4, "Z": 16 }, "uv": { "X": 176, "Y": 65 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"pig": {
|
||||
"textureSize": { "X": 64, "Y": 32 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 12, "Z": -6 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -4, "Z": -8 }, "size": { "X": 8, "Y": 8, "Z": 8 }, "uv": { "X": 0, "Y": 0 } },
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -9 }, "size": { "X": 4, "Y": 3, "Z": 1 }, "uv": { "X": 16, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"translation": { "X": 0, "Y": 11, "Z": 2 },
|
||||
"rotation": { "X": 90, "Y": 0, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -5, "Y": -10, "Z": -7 }, "size": { "X": 10, "Y": 16, "Z": 8 }, "uv": { "X": 28, "Y": 8 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg0",
|
||||
"translation": { "X": -3, "Y": 18, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg1",
|
||||
"translation": { "X": 3, "Y": 18, "Z": 7 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg2",
|
||||
"translation": { "X": -3, "Y": 18, "Z": -5 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leg3",
|
||||
"translation": { "X": 3, "Y": 18, "Z": -5 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": 0, "Z": -2 }, "size": { "X": 4, "Y": 6, "Z": 4 }, "uv": { "X": 0, "Y": 16 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"snowgolem": {
|
||||
"textureSize": { "X": 64, "Y": 64 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"translation": { "X": 0, "Y": 4, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -4, "Y": -8, "Z": -4 }, "size": { "X": 8, "Y": 8, "Z": 8 }, "uv": { "X": 0, "Y": 0 }, "inflate": -0.5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "arm1",
|
||||
"translation": { "X": 0, "Y": 6, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -1 }, "size": { "X": 12, "Y": 2, "Z": 2 }, "uv": { "X": 32, "Y": 0 }, "inflate": -0.5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "arm2",
|
||||
"translation": { "X": 0, "Y": 6, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -1 }, "size": { "X": 12, "Y": 2, "Z": 2 }, "uv": { "X": 32, "Y": 0 }, "inflate": -0.5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "piece1",
|
||||
"translation": { "X": 0, "Y": 13, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -5, "Y": -10, "Z": -5 }, "size": { "X": 10, "Y": 10, "Z": 10 }, "uv": { "X": 0, "Y": 16 }, "inflate": -0.5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "piece2",
|
||||
"translation": { "X": 0, "Y": 24, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -6, "Y": -12, "Z": -6 }, "size": { "X": 12, "Y": 12, "Z": 12 }, "uv": { "X": 0, "Y": 36 }, "inflate": -0.5 }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"wood": {
|
||||
"textureSize": { "X": 64, "Y": 64 },
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": -7, "Z": -1 }, "size": { "X": 2, "Y": 7, "Z": 2 }, "uv": { "X": 0, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -6, "Y": 0, "Z": -1.5 }, "size": { "X": 12, "Y": 3, "Z": 3 }, "uv": { "X": 0, "Y": 26 } },
|
||||
{ "pos": { "X": -3, "Y": 3, "Z": -1 }, "size": { "X": 2, "Y": 7, "Z": 2 }, "uv": { "X": 16, "Y": 0 }, "mirror": true },
|
||||
{ "pos": { "X": 1, "Y": 3, "Z": -1 }, "size": { "X": 2, "Y": 7, "Z": 2 }, "uv": { "X": 48, "Y": 16 } },
|
||||
{ "pos": { "X": -4, "Y": 10, "Z": -1 }, "size": { "X": 8, "Y": 2, "Z": 2 }, "uv": { "X": 0, "Y": 48 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightArm",
|
||||
"translation": { "X": -5, "Y": 2, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -2, "Y": -2, "Z": -1 }, "size": { "X": 2, "Y": 12, "Z": 2 }, "uv": { "X": 24, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftArm",
|
||||
"translation": { "X": 5, "Y": 2, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": 0, "Y": -2, "Z": -1 }, "size": { "X": 2, "Y": 12, "Z": 2 }, "uv": { "X": 32, "Y": 16 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightLeg",
|
||||
"translation": { "X": -1.9, "Y": 12, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -1 }, "size": { "X": 2, "Y": 11, "Z": 2 }, "uv": { "X": 8, "Y": 0 } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftLeg",
|
||||
"translation": { "X": 1.9, "Y": 12, "Z": 0 },
|
||||
"boxes": [
|
||||
{ "pos": { "X": -1, "Y": 0, "Z": -1 }, "size": { "X": 2, "Y": 11, "Z": 2 }, "uv": { "X": 40, "Y": 16 }, "mirror": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightItem",
|
||||
"translation": { "X": -6, "Y": 12, "Z": 0 },
|
||||
"boxes": []
|
||||
},
|
||||
{
|
||||
"name": "leftItem",
|
||||
"translation": { "X": 6, "Y": 12, "Z": 0 },
|
||||
"boxes": []
|
||||
},
|
||||
{
|
||||
"name": "base_plate",
|
||||
"boxes": [
|
||||
{ "pos": { "X": -6, "Y": 23, "Z": -6 }, "size": { "X": 12, "Y": 1, "Z": 12 }, "uv": { "X": 0, "Y": 32 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
764
PckStudio.ModelSupport/Resources/modelMetaData.json
Normal file
764
PckStudio.ModelSupport/Resources/modelMetaData.json
Normal file
@@ -0,0 +1,764 @@
|
||||
{
|
||||
"bat": {
|
||||
"textureLocations": [
|
||||
"res/mob/bat"
|
||||
],
|
||||
"materialName": "bat",
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"children": [
|
||||
{ "name": "rightEar" },
|
||||
{ "name": "leftEar" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"children": [
|
||||
{
|
||||
"name": "rightWing",
|
||||
"children": [
|
||||
{ "name": "rightWingTip" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftWing",
|
||||
"children": [
|
||||
{ "name": "leftWingTip" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bed": {
|
||||
"textureLocations": [
|
||||
"res/item/bed"
|
||||
],
|
||||
"uv_offsets": [ {"X": 0, "Y": 64} ]
|
||||
},
|
||||
"blaze": {
|
||||
"textureLocations": [
|
||||
"res/mob/fire"
|
||||
],
|
||||
"materialName": "blaze_head"
|
||||
},
|
||||
"boat": {
|
||||
"textureLocations": [
|
||||
"res/item/boat/boat_acacia",
|
||||
"res/item/boat/boat_birch",
|
||||
"res/item/boat/boat_darkoak",
|
||||
"res/item/boat/boat_jungle",
|
||||
"res/item/boat/boat_oak",
|
||||
"res/item/boat/boat_spruce"
|
||||
]
|
||||
},
|
||||
"chicken": {
|
||||
"textureLocations": [
|
||||
"res/mob/chicken"
|
||||
]
|
||||
},
|
||||
"cow": {
|
||||
"textureLocations": [
|
||||
"res/mob/cow"
|
||||
]
|
||||
},
|
||||
"creeper": {
|
||||
"textureLocations": [
|
||||
"res/mob/creeper"
|
||||
]
|
||||
},
|
||||
"creeper_head": {
|
||||
"textureLocations": [
|
||||
"res/mob/creeper"
|
||||
]
|
||||
},
|
||||
"dolphin": {
|
||||
"textureLocations": [
|
||||
"res/mob/dolphin"
|
||||
],
|
||||
"parts": [
|
||||
{
|
||||
"name": "body",
|
||||
"children": [
|
||||
{
|
||||
"name": "head",
|
||||
"children": [ { "name": "nose" } ]
|
||||
},
|
||||
{
|
||||
"name": "tail",
|
||||
"children": [ { "name": "tail_fin" } ]
|
||||
},
|
||||
{ "name": "right_fin" },
|
||||
{ "name": "left_fin" },
|
||||
{ "name": "back_fin" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dragon": {
|
||||
"textureLocations": [
|
||||
"res/mob/enderdragon/ender"
|
||||
],
|
||||
"materialName": "ender_dragon",
|
||||
"parts": [
|
||||
{ "name": "body" },
|
||||
// only needs to be inside when neck 1-5 aren't present
|
||||
{ "name": "neck" },
|
||||
|
||||
// neck 1-5 & tail 1-12 are not required to be inside the model
|
||||
{ "name": "neck1" },
|
||||
{ "name": "neck2" },
|
||||
{ "name": "neck3" },
|
||||
{ "name": "neck4" },
|
||||
{ "name": "neck5" },
|
||||
|
||||
{ "name": "tail1" },
|
||||
{ "name": "tail2" },
|
||||
{ "name": "tail3" },
|
||||
{ "name": "tail4" },
|
||||
{ "name": "tail5" },
|
||||
{ "name": "tail6" },
|
||||
{ "name": "tail7" },
|
||||
{ "name": "tail8" },
|
||||
{ "name": "tail9" },
|
||||
{ "name": "tail10" },
|
||||
{ "name": "tail11" },
|
||||
{ "name": "tail12" },
|
||||
|
||||
{
|
||||
"name": "head",
|
||||
"children": [ { "name": "jaw" } ]
|
||||
},
|
||||
{
|
||||
"name": "wing",
|
||||
"children": [ { "name": "wingtip" } ]
|
||||
},
|
||||
{
|
||||
"name": "wing1",
|
||||
"children": [ { "name": "wingtip1" } ]
|
||||
},
|
||||
{
|
||||
"name": "rearleg",
|
||||
"children": [
|
||||
{
|
||||
"name": "rearlegtip",
|
||||
"children": [ { "name": "rearfoot" } ]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rearleg1",
|
||||
"children": [
|
||||
{
|
||||
"name": "rearlegtip1",
|
||||
"children": [ { "name": "rearfoot1" } ]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "frontleg",
|
||||
"children": [
|
||||
{
|
||||
"name": "frontlegtip",
|
||||
"children": [ { "name": "frontfoot" } ]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "frontleg1",
|
||||
"children": [
|
||||
{
|
||||
"name": "frontlegtip1",
|
||||
"children": [ { "name": "frontfoot1" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dragon_head": {
|
||||
"textureLocations": [
|
||||
"res/mob/enderdragon/ender"
|
||||
],
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"children": [ { "name": "jaw" } ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"enderman": {
|
||||
"textureLocations": [
|
||||
"res/mob/enderman"
|
||||
],
|
||||
"materialName": "enderman" // "enderman_invisible" also valid
|
||||
},
|
||||
"ghast": {
|
||||
"textureLocations": [
|
||||
"res/mob/ghast",
|
||||
"res/mob/ghast_fire"
|
||||
],
|
||||
"materialName": "ghast"
|
||||
},
|
||||
"guardian": {
|
||||
"textureLocations": [
|
||||
"res/mob/guardian",
|
||||
"res/mob/guardian_elder"
|
||||
],
|
||||
"materialName": "guardian",
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"children": [
|
||||
{ "name": "eye" },
|
||||
{
|
||||
"name": "tailpart0",
|
||||
"children": [
|
||||
{
|
||||
"name": "tailpart1",
|
||||
"children": [ { "name": "tailpart2" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"irongolem": {
|
||||
"textureLocations": [
|
||||
"res/mob/villager_golem"
|
||||
],
|
||||
"materialName": "iron_golem"
|
||||
},
|
||||
"lavaslime": {
|
||||
"textureLocations": [
|
||||
"res/mob/lava"
|
||||
],
|
||||
"materialName": "magma_cube"
|
||||
},
|
||||
"llama": {
|
||||
"textureLocations": [
|
||||
"res/mob/llama/llama",
|
||||
"res/mob/llama/llama_brown",
|
||||
"res/mob/llama/llama_creamy",
|
||||
"res/mob/llama/llama_gray",
|
||||
"res/mob/llama/llama_white"
|
||||
]
|
||||
},
|
||||
"llamaspit": {
|
||||
"textureLocations": [
|
||||
"res/mob/llama/spit"
|
||||
]
|
||||
},
|
||||
"minecart": {
|
||||
"textureLocations": [
|
||||
"res/item/cart"
|
||||
]
|
||||
},
|
||||
// the ocelot model is weird.. -miku
|
||||
"ocelot": {
|
||||
"textureLocations": [
|
||||
"res/mob/ozelot"
|
||||
]
|
||||
},
|
||||
"parrot": {
|
||||
"textureLocations": [
|
||||
"res/mob/parrot/parrot_blue",
|
||||
"res/mob/parrot/parrot_green",
|
||||
"res/mob/parrot/parrot_grey",
|
||||
"res/mob/parrot/parrot_red_blue",
|
||||
"res/mob/parrot/parrot_yellow_blue"
|
||||
],
|
||||
"parts": [
|
||||
{
|
||||
"name": "head",
|
||||
"children": [
|
||||
{ "name": "head2" },
|
||||
{ "name": "beak1" },
|
||||
{ "name": "beak2" },
|
||||
{ "name": "feather" }
|
||||
]
|
||||
},
|
||||
{ "name": "body" },
|
||||
{ "name": "tail" },
|
||||
{ "name": "wing0" },
|
||||
{ "name": "wing1" },
|
||||
{ "name": "leg0" },
|
||||
{ "name": "leg1" }
|
||||
]
|
||||
},
|
||||
"phantom": {
|
||||
"textureLocations": [
|
||||
"res/mob/phantom"
|
||||
],
|
||||
"materialName": "phantom", // phantom_invisible is also valid
|
||||
"parts": [
|
||||
{
|
||||
"name": "body",
|
||||
"children": [
|
||||
{ "name": "head" },
|
||||
{
|
||||
"name": "wing0",
|
||||
"children": [ { "name": "wingtip0" } ]
|
||||
},
|
||||
{
|
||||
"name": "wing1",
|
||||
"children": [ { "name": "wingtip1" } ]
|
||||
},
|
||||
{
|
||||
"name": "tail",
|
||||
"children": [ { "name": "tailtip" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"pig": {
|
||||
"textureLocations": [
|
||||
"res/mob/pig",
|
||||
"res/mob/saddle"
|
||||
]
|
||||
},
|
||||
"pigzombie": {
|
||||
"textureLocations": [
|
||||
"res/mob/pigzombie"
|
||||
],
|
||||
"materialName": "zombie_pigman"
|
||||
},
|
||||
"polarbear": {
|
||||
"textureLocations": [
|
||||
"res/mob/bear/polarbear"
|
||||
]
|
||||
},
|
||||
"rabbit": {
|
||||
"textureLocations": [
|
||||
"res/mob/rabbit/black",
|
||||
"res/mob/rabbit/brown",
|
||||
"res/mob/rabbit/caerbannog",
|
||||
"res/mob/rabbit/gold",
|
||||
"res/mob/rabbit/salt",
|
||||
"res/mob/rabbit/toast",
|
||||
"res/mob/rabbit/white",
|
||||
"res/mob/rabbit/white_splotched"
|
||||
]
|
||||
},
|
||||
"sheep": {
|
||||
"textureLocations": [
|
||||
"res/mob/sheep",
|
||||
"res/mob/sheep_fur"
|
||||
],
|
||||
"materialName": "sheep"
|
||||
},
|
||||
"sheep.sheared": {
|
||||
"textureLocations": [
|
||||
"res/mob/sheep"
|
||||
],
|
||||
"materialName": "sheep"
|
||||
},
|
||||
"shulker": {
|
||||
"textureLocations": [
|
||||
"res/mob/shulker/endergolem",
|
||||
"res/mob/shulker/spark"
|
||||
],
|
||||
"materialName": "shulker"
|
||||
},
|
||||
"silverfish": {
|
||||
"textureLocations": [
|
||||
"res/mob/silverfish"
|
||||
]
|
||||
},
|
||||
"skeleton": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton"
|
||||
],
|
||||
"materialName": "skeleton"
|
||||
},
|
||||
"skeleton.stray": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton/stray"
|
||||
],
|
||||
"materialName": "stray"
|
||||
},
|
||||
"skeleton.wither": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton_wither"
|
||||
],
|
||||
"materialName": "wither_skeleton"
|
||||
},
|
||||
"slime": {
|
||||
"textureLocations": [
|
||||
"res/mob/slime"
|
||||
]
|
||||
},
|
||||
"slime.armor": {
|
||||
"textureLocations": [
|
||||
"res/mob/slime"
|
||||
]
|
||||
},
|
||||
"snowgolem": {
|
||||
"textureLocations": [
|
||||
"res/mob/snowman"
|
||||
]
|
||||
},
|
||||
"spider": {
|
||||
"textureLocations": [
|
||||
"res/mob/spider",
|
||||
"res/mob/cavespider"
|
||||
],
|
||||
"materialName": "spider" // "spider_invisible" also valid
|
||||
},
|
||||
"squid": {
|
||||
"textureLocations": [
|
||||
"res/mob/squid"
|
||||
]
|
||||
},
|
||||
"trident": {
|
||||
"textureLocations": [
|
||||
"res/item/trident"
|
||||
]
|
||||
},
|
||||
"turtle": {
|
||||
"textureLocations": [
|
||||
"res/mob/sea_turtle"
|
||||
]
|
||||
},
|
||||
"villager": {
|
||||
"textureLocations": [
|
||||
"res/mob/villager/villager",
|
||||
"res/mob/villager/butcher",
|
||||
"res/mob/villager/farmer",
|
||||
"res/mob/villager/librarian",
|
||||
"res/mob/villager/priest",
|
||||
"res/mob/villager/smith"
|
||||
]
|
||||
},
|
||||
"villager.witch": {
|
||||
"textureLocations": [
|
||||
"res/mob/witch"
|
||||
]
|
||||
},
|
||||
"vex": {
|
||||
"textureLocations": [
|
||||
"res/mob/illager/vex",
|
||||
"res/mob/illager/vex_charging"
|
||||
]
|
||||
},
|
||||
"evoker": {
|
||||
"textureLocations": [
|
||||
"res/mob/illager/evoker"
|
||||
]
|
||||
},
|
||||
"vindicator": {
|
||||
"textureLocations": [
|
||||
"res/mob/illager/vindicator"
|
||||
]
|
||||
},
|
||||
"witherBoss": {
|
||||
"textureLocations": [
|
||||
"res/mob/wither/wither",
|
||||
"res/mob/wither/wither_invulnerable"
|
||||
],
|
||||
"materialName": "wither_boss"
|
||||
},
|
||||
"wolf": {
|
||||
"textureLocations": [
|
||||
"res/mob/wolf",
|
||||
"res/mob/wolf_angry",
|
||||
"res/mob/wolf_tame"
|
||||
],
|
||||
"materialName": "wolf"
|
||||
},
|
||||
"zombie": {
|
||||
"textureLocations": [
|
||||
"res/mob/zombie"
|
||||
]
|
||||
},
|
||||
"zombie.husk": {
|
||||
"textureLocations": [
|
||||
"res/mob/zombie/husk"
|
||||
]
|
||||
},
|
||||
"zombie.villager": {
|
||||
"textureLocations": [
|
||||
"res/mob/zombie_villager/zombie_villager",
|
||||
"res/mob/zombie_villager/zombie_butcher",
|
||||
"res/mob/zombie_villager/zombie_farmer",
|
||||
"res/mob/zombie_villager/zombie_librarian",
|
||||
"res/mob/zombie_villager/zombie_priest",
|
||||
"res/mob/zombie_villager/zombie_smith"
|
||||
]
|
||||
},
|
||||
"horse.v2": {
|
||||
// markings and armor not included
|
||||
"textureLocations": [
|
||||
"res/mob/horse/donkey",
|
||||
"res/mob/horse/horse_black",
|
||||
"res/mob/horse/horse_brown",
|
||||
"res/mob/horse/horse_chestnut",
|
||||
"res/mob/horse/horse_creamy",
|
||||
"res/mob/horse/horse_darkbrown",
|
||||
"res/mob/horse/horse_gray",
|
||||
"res/mob/horse/horse_skeleton",
|
||||
"res/mob/horse/horse_white",
|
||||
"res/mob/horse/horse_zombie",
|
||||
"res/mob/horse/mule"
|
||||
],
|
||||
"parts": [
|
||||
{
|
||||
"name": "Neck",
|
||||
"children": [
|
||||
{
|
||||
"name": "Head",
|
||||
"children": [
|
||||
{ "name": "HeadSaddle" },
|
||||
{ "name": "UMouth" },
|
||||
{ "name": "Ear1" },
|
||||
{ "name": "Ear2" },
|
||||
{ "name": "MuleEarL" },
|
||||
{ "name": "MuleEarR" },
|
||||
{ "name": "SaddleMouthL" },
|
||||
{ "name": "SaddleMouthR" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Body",
|
||||
"children": [
|
||||
{ "name": "TailA" },
|
||||
{ "name": "Saddle" }
|
||||
]
|
||||
},
|
||||
{ "name": "Mane" },
|
||||
{ "name": "Leg1A" },
|
||||
{ "name": "Leg2A" },
|
||||
{ "name": "Leg3A" },
|
||||
{ "name": "Leg4A" },
|
||||
{ "name": "Bag1" },
|
||||
{ "name": "Bag2" },
|
||||
{ "name": "SaddleMouthLine" },
|
||||
{ "name": "SaddleMouthLineR" }
|
||||
]
|
||||
},
|
||||
"cat": {
|
||||
"textureLocations": [
|
||||
"res/mob/cat_black",
|
||||
"res/mob/cat_red",
|
||||
"res/mob/cat_siamese"
|
||||
]
|
||||
},
|
||||
"zombie.drowned": {
|
||||
"textureLocations": [
|
||||
"res/mob/zombie/drowned"
|
||||
],
|
||||
"materialName": "drowned"
|
||||
},
|
||||
"endermite": {
|
||||
"textureLocations": [
|
||||
"res/mob/endermite"
|
||||
]
|
||||
},
|
||||
"cod": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/cod"
|
||||
]
|
||||
},
|
||||
"pufferfish.small": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/pufferfish"
|
||||
]
|
||||
},
|
||||
"pufferfish.mid": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/pufferfish"
|
||||
]
|
||||
},
|
||||
"pufferfish.large": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/pufferfish"
|
||||
]
|
||||
},
|
||||
"salmon": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/salmon"
|
||||
]
|
||||
},
|
||||
"skeleton_head": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton"
|
||||
]
|
||||
},
|
||||
"skeleton_wither_head": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton_wither"
|
||||
]
|
||||
},
|
||||
"stray.armor": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton/stray_overlay"
|
||||
]
|
||||
},
|
||||
"stray_armor": {
|
||||
"textureLocations": [
|
||||
"res/mob/skeleton/stray_overlay"
|
||||
]
|
||||
},
|
||||
"tropicalfish_a": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/tropical_a",
|
||||
"res/mob/fish/tropical_a_pattern_1",
|
||||
"res/mob/fish/tropical_a_pattern_2",
|
||||
"res/mob/fish/tropical_a_pattern_3",
|
||||
"res/mob/fish/tropical_a_pattern_4",
|
||||
"res/mob/fish/tropical_a_pattern_5",
|
||||
"res/mob/fish/tropical_a_pattern_6"
|
||||
]
|
||||
},
|
||||
"tropicalfish_b": {
|
||||
"textureLocations": [
|
||||
"res/mob/fish/tropical_b",
|
||||
"res/mob/fish/tropical_b_pattern_1",
|
||||
"res/mob/fish/tropical_b_pattern_2",
|
||||
"res/mob/fish/tropical_b_pattern_3",
|
||||
"res/mob/fish/tropical_b_pattern_4",
|
||||
"res/mob/fish/tropical_b_pattern_5",
|
||||
"res/mob/fish/tropical_b_pattern_6"
|
||||
]
|
||||
},
|
||||
"zombie_head": {
|
||||
"textureLocations": [
|
||||
"res/mob/zombie"
|
||||
]
|
||||
},
|
||||
"mooshroom": {
|
||||
"textureLocations": [
|
||||
"res/mob/redcow"
|
||||
]
|
||||
},
|
||||
"witherBoss.armor": {
|
||||
"textureLocations": [
|
||||
"res/mob/wither/wither_armor"
|
||||
]
|
||||
},
|
||||
|
||||
"villager_v2": {
|
||||
"textureLocations": [
|
||||
"res/mob/villager/villagerBase1",
|
||||
"res/mob/villager/villagerBase2",
|
||||
"res/mob/villager/villagerBase3",
|
||||
"res/mob/villager/villagerBase4",
|
||||
"res/mob/villager/villagerBase5",
|
||||
"res/mob/villager/villagerBase6",
|
||||
|
||||
"res/mob/wandering_trader",
|
||||
|
||||
"res/mob/villager/professions/armorer",
|
||||
"res/mob/villager/professions/butcher",
|
||||
"res/mob/villager/professions/cartographer",
|
||||
"res/mob/villager/professions/cleric",
|
||||
"res/mob/villager/professions/farmer",
|
||||
"res/mob/villager/professions/fisherman",
|
||||
"res/mob/villager/professions/fletcher",
|
||||
"res/mob/villager/professions/leatherworker",
|
||||
"res/mob/villager/professions/librarian",
|
||||
"res/mob/villager/professions/nitwit",
|
||||
"res/mob/villager/professions/shepherd",
|
||||
"res/mob/villager/professions/stonemason",
|
||||
"res/mob/villager/professions/toolsmith",
|
||||
"res/mob/villager/professions/unskilled",
|
||||
"res/mob/villager/professions/weaponsmith",
|
||||
|
||||
"res/mob/villager/biomes/biome_desert",
|
||||
"res/mob/villager/biomes/biome_jungle",
|
||||
"res/mob/villager/biomes/biome_plains",
|
||||
"res/mob/villager/biomes/biome_savanna",
|
||||
"res/mob/villager/biomes/biome_snow",
|
||||
"res/mob/villager/biomes/biome_swamp",
|
||||
"res/mob/villager/biomes/biome_taiga",
|
||||
|
||||
"res/mob/villager/levels/level_diamond",
|
||||
"res/mob/villager/levels/level_gold",
|
||||
"res/mob/villager/levels/level_iron"
|
||||
]
|
||||
},
|
||||
"zombie.villager_v2": {
|
||||
"textureLocations": [
|
||||
"res/mob/zombie_villager/zombie_villager",
|
||||
"res/mob/zombie_villager/zombie_butcher",
|
||||
"res/mob/zombie_villager/zombie_farmer",
|
||||
"res/mob/zombie_villager/zombie_librarian",
|
||||
"res/mob/zombie_villager/zombie_priest",
|
||||
"res/mob/zombie_villager/zombie_smith",
|
||||
|
||||
"res/mob/zombie_villager/biomes/biome-desert-zombie",
|
||||
"res/mob/zombie_villager/biomes/biome-jungle-zombie",
|
||||
"res/mob/zombie_villager/biomes/biome-plains-zombie",
|
||||
"res/mob/zombie_villager/biomes/biome-savanna-zombie",
|
||||
"res/mob/zombie_villager/biomes/biome-snow-zombie",
|
||||
"res/mob/zombie_villager/biomes/biome-swamp-zombie",
|
||||
"res/mob/zombie_villager/biomes/biome-taiga-zombie",
|
||||
|
||||
"res/mob/zombie_villager/professions/armorer",
|
||||
"res/mob/zombie_villager/professions/butcher",
|
||||
"res/mob/zombie_villager/professions/cartographer",
|
||||
"res/mob/zombie_villager/professions/cleric",
|
||||
"res/mob/zombie_villager/professions/farmer",
|
||||
"res/mob/zombie_villager/professions/fisherman",
|
||||
"res/mob/zombie_villager/professions/fletcher",
|
||||
"res/mob/zombie_villager/professions/leatherworker",
|
||||
"res/mob/zombie_villager/professions/librarian",
|
||||
"res/mob/zombie_villager/professions/nitwit",
|
||||
"res/mob/zombie_villager/professions/shepherd",
|
||||
"res/mob/zombie_villager/professions/stonemason",
|
||||
"res/mob/zombie_villager/professions/toolsmith",
|
||||
"res/mob/zombie_villager/professions/weaponsmith"
|
||||
]
|
||||
},
|
||||
"pillager": {
|
||||
"textureLocations": [
|
||||
"res/mob/pillager"
|
||||
]
|
||||
},
|
||||
"ravager": {
|
||||
"textureLocations": [
|
||||
"res/mob/illager/ravager"
|
||||
]
|
||||
},
|
||||
|
||||
"panda": {
|
||||
"textureLocations": [
|
||||
"res/mob/panda/panda",
|
||||
"res/mob/panda/panda_aggressive",
|
||||
"res/mob/panda/panda_brown",
|
||||
"res/mob/panda/panda_lazy",
|
||||
"res/mob/panda/panda_playful",
|
||||
"res/mob/panda/panda_sneezy",
|
||||
"res/mob/panda/panda_worried"
|
||||
]
|
||||
},
|
||||
|
||||
"wood": {
|
||||
"textureLocations": [ "res/item/armorstand/wood" ],
|
||||
"parts": [
|
||||
{ "name": "head" },
|
||||
{ "name": "body" },
|
||||
{
|
||||
"name": "rightArm",
|
||||
"children": [{ "name": "rightItem" }]
|
||||
},
|
||||
{
|
||||
"name": "leftArm",
|
||||
"children": [{ "name": "leftItem" }]
|
||||
},
|
||||
{
|
||||
"name": "rightLeg"
|
||||
},
|
||||
{
|
||||
"name": "leftLeg"
|
||||
},
|
||||
{
|
||||
"name": "base_plate"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
553
PckStudio.ModelSupport/SkinModelImporter.cs
Normal file
553
PckStudio.ModelSupport/SkinModelImporter.cs
Normal file
@@ -0,0 +1,553 @@
|
||||
/* 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.IO;
|
||||
using System.Linq;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PckStudio.Core;
|
||||
using PckStudio.Core.Skin;
|
||||
using PckStudio.Core.Extensions;
|
||||
using PckStudio.Core.FileFormats;
|
||||
using PckStudio.ModelSupport.Format.External;
|
||||
using PckStudio.Core.Additional_Popups;
|
||||
using PckStudio.ModelSupport.Internal.Format;
|
||||
|
||||
namespace PckStudio.ModelSupport
|
||||
{
|
||||
public sealed class SkinModelImporter : ModelImporter<SkinModelInfo>
|
||||
{
|
||||
public static SkinModelImporter Default { get; } = new SkinModelImporter();
|
||||
|
||||
private SkinModelImporter()
|
||||
{
|
||||
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 SkinModelInfo ImportPsm(string filepath)
|
||||
{
|
||||
var reader = new PSMFileReader();
|
||||
PSMFile csmbFile = reader.FromFile(filepath);
|
||||
return new SkinModelInfo(null, csmbFile.SkinANIM, new(csmbFile.Parts, csmbFile.Offsets));
|
||||
}
|
||||
|
||||
internal static void ExportPsm(string filepath, SkinModelInfo modelInfo)
|
||||
{
|
||||
PSMFile psmFile = new PSMFile(PSMFile.CurrentVersion, modelInfo.Anim);
|
||||
psmFile.Parts.AddRange(modelInfo.Model.AdditionalBoxes);
|
||||
psmFile.Offsets.AddRange(modelInfo.Model.PartOffsets);
|
||||
var writer = new PSMFileWriter(psmFile);
|
||||
writer.WriteToFile(filepath);
|
||||
}
|
||||
|
||||
internal static SkinModelInfo ImportBlockBenchModel(string filepath)
|
||||
{
|
||||
BlockBenchModel blockBenchModel = JsonConvert.DeserializeObject<BlockBenchModel>(File.ReadAllText(filepath));
|
||||
if (!blockBenchModel.Format.UseBoxUv)
|
||||
{
|
||||
Trace.TraceError($"[{nameof(SkinModelImporter)}:{nameof(ImportBlockBenchModel)}] Failed to import skin '{blockBenchModel.Name}': Skin does not use box uv.");
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<SkinPartOffset> partOffsets = blockBenchModel.Outliner
|
||||
.Where(token => token.Type == JTokenType.Object && SkinBOX.IsValidType(TryConvertToSkinBoxType(token.ToObject<Outline>().Name)))
|
||||
.Select(token => token.ToObject<Outline>())
|
||||
.Select(outline => new SkinPartOffset(TryConvertToSkinBoxType(outline.Name), -GetOffsetFromOrigin(TryConvertToSkinBoxType(outline.Name), outline.Origin).Y))
|
||||
.Where(offset => offset.Value != 0f);
|
||||
|
||||
IEnumerable<SkinBOX> boxes = ReadOutliner(null, blockBenchModel.Outliner, blockBenchModel.Elements);
|
||||
|
||||
Image texture = null;
|
||||
if (blockBenchModel.Textures.IndexInRange(0))
|
||||
{
|
||||
texture = blockBenchModel.Textures[0];
|
||||
texture = SwapBoxBottomTexture(texture, boxes);
|
||||
}
|
||||
|
||||
return CreateSkinModelInfo(texture, boxes, partOffsets);
|
||||
}
|
||||
|
||||
private static SkinModelInfo CreateSkinModelInfo(Image texture, IEnumerable<SkinBOX> boxes, IEnumerable<SkinPartOffset> partOffsets)
|
||||
{
|
||||
SkinANIM skinANIM = (
|
||||
SkinAnimMask.HEAD_DISABLED |
|
||||
SkinAnimMask.HEAD_OVERLAY_DISABLED |
|
||||
SkinAnimMask.BODY_DISABLED |
|
||||
SkinAnimMask.BODY_OVERLAY_DISABLED |
|
||||
SkinAnimMask.RIGHT_ARM_DISABLED |
|
||||
SkinAnimMask.RIGHT_ARM_OVERLAY_DISABLED |
|
||||
SkinAnimMask.LEFT_ARM_DISABLED |
|
||||
SkinAnimMask.LEFT_ARM_OVERLAY_DISABLED |
|
||||
SkinAnimMask.RIGHT_LEG_DISABLED |
|
||||
SkinAnimMask.RIGHT_LEG_OVERLAY_DISABLED |
|
||||
SkinAnimMask.LEFT_LEG_DISABLED |
|
||||
SkinAnimMask.LEFT_LEG_OVERLAY_DISABLED);
|
||||
|
||||
skinANIM = skinANIM.SetFlag(SkinAnimFlag.RESOLUTION_64x64, texture.Size.Width == texture.Size.Height);
|
||||
|
||||
SkinModel skinModel = new SkinModel();
|
||||
|
||||
skinModel.PartOffsets.AddRange(partOffsets);
|
||||
|
||||
SkinBOX ApplyOffset(SkinBOX box)
|
||||
{
|
||||
SkinPartOffset offset = skinModel.PartOffsets.FirstOrDefault(offset => offset.Type == (box.IsOverlayPart() ? box.GetBaseType() : box.Type));
|
||||
return string.IsNullOrEmpty(offset.Type) ? box : new SkinBOX(box.Type, box.Pos - (Vector3.UnitY * offset.Value), box.Size, box.UV, box.HideWithArmor, box.Mirror, box.Scale);
|
||||
}
|
||||
|
||||
IEnumerable<SkinBOX> convertedBoxes = boxes.Select(ApplyOffset);
|
||||
|
||||
IEnumerable<SkinBOX> customBoxes = convertedBoxes.Where(box => !SkinBOX.KnownHashes.ContainsKey(box.GetHashCode()));
|
||||
|
||||
skinModel.AdditionalBoxes.AddRange(customBoxes);
|
||||
|
||||
// check for know boxes and filter them out
|
||||
SkinAnimMask mask = (SkinAnimMask)convertedBoxes
|
||||
.Where(box => SkinBOX.KnownHashes.ContainsKey(box.GetHashCode()) && Enum.IsDefined(typeof(SkinAnimMask), (1 >> (int)SkinBOX.KnownHashes[box.GetHashCode()])))
|
||||
.Select(box => SkinBOX.KnownHashes[box.GetHashCode()])
|
||||
.Select(i => 1 << (int)i)
|
||||
.DefaultIfEmpty()
|
||||
.Aggregate((a, b) => a | b);
|
||||
|
||||
if (mask != SkinAnimMask.NONE)
|
||||
skinANIM &= ~mask;
|
||||
|
||||
return new SkinModelInfo(texture, skinANIM, skinModel);
|
||||
}
|
||||
|
||||
private static IEnumerable<SkinBOX> ReadOutliner(string parentName, JArray oulineChildren, IReadOnlyCollection<Element> elements)
|
||||
{
|
||||
IEnumerable<SkinBOX> boxes = oulineChildren
|
||||
.Where(token => token.Type == JTokenType.String && Guid.TryParse(token.ToString(), out Guid elementUuid) && elements.Any(e => e.Uuid == elementUuid))
|
||||
.Select(token => elements.First(e => Guid.Parse(token.ToString()) == e.Uuid))
|
||||
.Where(element => element.Type == "cube" && element.UseBoxUv && element.Export && SkinBOX.IsValidType(TryConvertToSkinBoxType(parentName ?? element.Name)))
|
||||
.Select(element => LoadElement(element, TryConvertToSkinBoxType(parentName ?? element.Name)));
|
||||
|
||||
IEnumerable<Outline> childOutlines = oulineChildren
|
||||
.Where(token => token.Type == JTokenType.Object)
|
||||
.Select(token => token.ToObject<Outline>());
|
||||
|
||||
foreach (Outline childOutline in childOutlines)
|
||||
{
|
||||
boxes = boxes.Concat(ReadOutliner(parentName ?? childOutline.Name, childOutline.Children, elements));
|
||||
}
|
||||
return boxes;
|
||||
}
|
||||
|
||||
private static SkinBOX LoadElement(Element element, string outlineName)
|
||||
{
|
||||
var boundingBox = new BoundingBox(element.From, element.To);
|
||||
Vector3 pos = boundingBox.Start.ToNumericsVector();
|
||||
Vector3 size = boundingBox.Volume.ToNumericsVector();
|
||||
Vector2 uv = element.UvOffset;
|
||||
|
||||
pos = TranslateToInternalPosition(outlineName, pos, size, new Vector3(1, 1, 0));
|
||||
|
||||
var box = new SkinBOX(outlineName, pos, size, uv, mirror: element.MirrorUv);
|
||||
if (SkinBOX.IsBasePart(outlineName) && ((outlineName == "HEAD" && element.Inflate == 0.5f) || (element.Inflate >= 0.25f && element.Inflate <= 0.5f)))
|
||||
box = new SkinBOX(SkinBOXExtensions.GetOverlayType(outlineName), pos, size, uv, mirror: element.MirrorUv);
|
||||
return box;
|
||||
}
|
||||
|
||||
internal static void ExportBlockBenchModel(string filepath, SkinModelInfo modelInfo)
|
||||
{
|
||||
Image exportTexture = SwapBoxBottomTexture(modelInfo);
|
||||
BlockBenchModel blockBenchModel = BlockBenchModel.Create(BlockBenchFormatInfos.BedrockEntity, Path.GetFileNameWithoutExtension(filepath), new Size(64, exportTexture.Width == exportTexture.Height ? 64 : 32), [exportTexture]);
|
||||
|
||||
Dictionary<string, Outline> outliners = new Dictionary<string, Outline>(5);
|
||||
List<Element> elements = new List<Element>(modelInfo.Model.AdditionalBoxes.Count);
|
||||
|
||||
Dictionary<string, SkinPartOffset> offsetLookUp = new Dictionary<string, SkinPartOffset>(5);
|
||||
|
||||
void AddElement(SkinBOX box)
|
||||
{
|
||||
string offsetType = box.IsOverlayPart() ? box.GetBaseType() : box.Type;
|
||||
|
||||
Vector3 offset = GetOffsetForPart(offsetType, ref offsetLookUp, modelInfo.Model.PartOffsets);
|
||||
if (!outliners.ContainsKey(offsetType))
|
||||
{
|
||||
outliners.Add(offsetType, new Outline(offsetType)
|
||||
{
|
||||
Origin = GetSkinPartPivot(offsetType, new Vector3(1, 1, 0)) + offset
|
||||
});
|
||||
}
|
||||
|
||||
Element element = CreateElement(box);
|
||||
|
||||
element.From += offset;
|
||||
element.To += offset;
|
||||
|
||||
elements.Add(element);
|
||||
outliners[offsetType].Children.Add(element.Uuid);
|
||||
}
|
||||
|
||||
ANIM2BOX(modelInfo.Anim, AddElement);
|
||||
|
||||
foreach (SkinBOX box in modelInfo.Model.AdditionalBoxes)
|
||||
{
|
||||
AddElement(box);
|
||||
}
|
||||
blockBenchModel.Elements = elements.ToArray();
|
||||
blockBenchModel.Outliner = JArray.FromObject(outliners.Values);
|
||||
|
||||
string content = JsonConvert.SerializeObject(blockBenchModel);
|
||||
File.WriteAllText(filepath, content);
|
||||
}
|
||||
|
||||
private static Element CreateElement(SkinBOX box)
|
||||
{
|
||||
Vector3 transformPos = TranslateFromInternalPosistion(box, new Vector3(1, 1, 0));
|
||||
Element element = CreateElement(box.UV, transformPos, box.Size, box.Scale, box.Mirror);
|
||||
if (box.IsOverlayPart())
|
||||
element.Inflate = box.Type == "HEADWEAR" ? 0.5f : 0.25f;
|
||||
return element;
|
||||
}
|
||||
|
||||
private static Element CreateElement(Vector2 uvOffset, Vector3 pos, Vector3 size, float inflate, bool mirror)
|
||||
{
|
||||
return Element.CreateCube("cube", uvOffset, pos, size, inflate, mirror);
|
||||
}
|
||||
|
||||
private static Geometry GetGeometry(string filepath)
|
||||
{
|
||||
// Bedrock Entity (Model)
|
||||
if (filepath.EndsWith(".geo.json"))
|
||||
{
|
||||
BedrockModel bedrockModel = JsonConvert.DeserializeObject<BedrockModel>(File.ReadAllText(filepath));
|
||||
var availableModels = bedrockModel.Models.Select(m => m.Description.Identifier).ToArray();
|
||||
if (availableModels.Length < 2)
|
||||
return availableModels.Length == 1 ? bedrockModel.Models[0] : null;
|
||||
|
||||
using ItemSelectionPopUp itemSelectionPopUp = new ItemSelectionPopUp(availableModels);
|
||||
if (itemSelectionPopUp.ShowDialog() == DialogResult.OK && bedrockModel.Models.IndexInRange(itemSelectionPopUp.SelectedIndex))
|
||||
{
|
||||
return bedrockModel.Models[itemSelectionPopUp.SelectedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Bedrock Legacy Model
|
||||
else if (filepath.EndsWith(".json"))
|
||||
{
|
||||
BedrockLegacyModel bedrockModel = JsonConvert.DeserializeObject<BedrockLegacyModel>(File.ReadAllText(filepath));
|
||||
var availableModels = bedrockModel.Select(m => m.Key).ToArray();
|
||||
if (availableModels.Length < 2)
|
||||
return availableModels.Length == 1 ? bedrockModel[availableModels[0]] : null;
|
||||
using ItemSelectionPopUp itemSelectionPopUp = new ItemSelectionPopUp(availableModels);
|
||||
if (itemSelectionPopUp.ShowDialog() == DialogResult.OK && bedrockModel.ContainsKey(itemSelectionPopUp.SelectedItem))
|
||||
{
|
||||
return bedrockModel[itemSelectionPopUp.SelectedItem];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static SkinModelInfo ImportBedrockJson(string filepath)
|
||||
{
|
||||
Geometry geometry = GetGeometry(filepath);
|
||||
if (geometry is null)
|
||||
return null;
|
||||
|
||||
(IEnumerable<SkinBOX> boxes, IEnumerable<SkinPartOffset> partOffsets) = LoadGeometry(geometry);
|
||||
|
||||
Image texture = null;
|
||||
string texturePath = Path.Combine(Path.GetDirectoryName(filepath), Path.GetFileNameWithoutExtension(filepath)) + ".png";
|
||||
if (File.Exists(texturePath))
|
||||
{
|
||||
texture = Image.FromFile(texturePath).ReleaseFromFile();
|
||||
texture = SwapBoxBottomTexture(texture, boxes);
|
||||
}
|
||||
|
||||
return CreateSkinModelInfo(texture, boxes, partOffsets);
|
||||
}
|
||||
|
||||
private static (IEnumerable<SkinBOX> boxes, IEnumerable<SkinPartOffset> partOffsets) LoadGeometry(Geometry geometry)
|
||||
{
|
||||
List<SkinPartOffset> skinPartOffsets = new List<SkinPartOffset>();
|
||||
List<SkinBOX> boxes = new List<SkinBOX>();
|
||||
|
||||
foreach (Bone bone in geometry.Bones)
|
||||
{
|
||||
string boxType = TryConvertToSkinBoxType(bone.Name);
|
||||
if (!SkinBOX.IsValidType(boxType))
|
||||
continue;
|
||||
|
||||
string offsetType = SkinBOX.IsOverlayPart(boxType) ? SkinBOXExtensions.GetBaseType(boxType) : boxType;
|
||||
Vector3 offset = GetOffsetFromOrigin(offsetType, bone.Pivot * new Vector3(-1, 1, 1));
|
||||
if (offset.Y != 0f)
|
||||
skinPartOffsets.Add(new SkinPartOffset(offsetType, -offset.Y));
|
||||
|
||||
foreach (Cube cube in bone.Cubes)
|
||||
{
|
||||
Vector3 pos = TranslateToInternalPosition(boxType, cube.Origin, cube.Size, Vector3.UnitY);
|
||||
var skinBox = new SkinBOX(boxType, pos, cube.Size, cube.Uv, hideWithArmor: bone.Name == "helmet", mirror: cube.Mirror);
|
||||
if (SkinBOX.IsBasePart(boxType) && ((boxType == "HEAD" && cube.Inflate == 0.5f) || (cube.Inflate >= 0.25f && cube.Inflate <= 0.5f)))
|
||||
skinBox = new SkinBOX(SkinBOXExtensions.GetOverlayType(boxType), pos, cube.Size, cube.Uv, hideWithArmor: bone.Name == "helmet", mirror: cube.Mirror);
|
||||
boxes.Add(skinBox);
|
||||
}
|
||||
}
|
||||
return (boxes, skinPartOffsets);
|
||||
}
|
||||
|
||||
internal static void ExportBedrockJson(string filepath, SkinModelInfo modelInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filepath) || !filepath.EndsWith(".json"))
|
||||
return;
|
||||
|
||||
Dictionary<string, Bone> bones = new Dictionary<string, Bone>(5);
|
||||
Dictionary<string, SkinPartOffset> offsetLookUp = new Dictionary<string, SkinPartOffset>(5);
|
||||
|
||||
void AddBone(SkinBOX box)
|
||||
{
|
||||
string offsetType = box.IsOverlayPart() ? box.GetBaseType() : box.Type;
|
||||
|
||||
Vector3 offset = GetOffsetForPart(offsetType, ref offsetLookUp, modelInfo.Model.PartOffsets);
|
||||
|
||||
if (!bones.ContainsKey(offsetType))
|
||||
{
|
||||
Bone bone = new Bone(offsetType)
|
||||
{
|
||||
Pivot = GetSkinPartPivot(offsetType, new Vector3(0, 1, 0)) + offset
|
||||
};
|
||||
bones.Add(offsetType, bone);
|
||||
}
|
||||
Vector3 pivot = bones.ContainsKey(offsetType) ? bones[offsetType].Pivot : Vector3.Zero;
|
||||
Vector3 pos = TranslateFromInternalPosistion(box, new Vector3(1, 1, 0));
|
||||
pos = TransformSpace(pos, box.Size, new Vector3(1, 0, 0));
|
||||
|
||||
bones[offsetType].Cubes.Add(new Cube()
|
||||
{
|
||||
Origin = pos + offset,
|
||||
Size = box.Size,
|
||||
Uv = box.UV,
|
||||
Inflate = box.Scale + (box.IsOverlayPart() ? box.Type == "HEAD" ? 0.5f : 0.25f : 0f),
|
||||
Mirror = box.Mirror,
|
||||
});
|
||||
}
|
||||
|
||||
ANIM2BOX(modelInfo.Anim, AddBone);
|
||||
|
||||
foreach (SkinBOX box in modelInfo.Model.AdditionalBoxes)
|
||||
{
|
||||
AddBone(box);
|
||||
}
|
||||
|
||||
Geometry selectedGeometry = new Geometry();
|
||||
selectedGeometry.Bones.AddRange(bones.Values);
|
||||
object bedrockModel = null;
|
||||
// Bedrock Entity (Model)
|
||||
if (filepath.EndsWith(".geo.json"))
|
||||
{
|
||||
selectedGeometry.Description = new GeometryDescription()
|
||||
{
|
||||
Identifier = $"geometry.{Application.ProductName}.{Path.GetFileNameWithoutExtension(filepath)}",
|
||||
TextureSize = modelInfo.Texture.Size,
|
||||
};
|
||||
bedrockModel = new BedrockModel
|
||||
{
|
||||
FormatVersion = "1.12.0",
|
||||
Models = { selectedGeometry }
|
||||
};
|
||||
}
|
||||
// Bedrock Legacy Model
|
||||
else if (filepath.EndsWith(".json") && modelInfo.Texture.Height == modelInfo.Texture.Width)
|
||||
{
|
||||
bedrockModel = new BedrockLegacyModel
|
||||
{
|
||||
{ $"geometry.{Application.ProductName}.{Path.GetFileNameWithoutExtension(filepath)}", selectedGeometry }
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Can't export to Bedrock Legacy Model.", "Invalid Texture Dimensions", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bedrockModel is not null)
|
||||
{
|
||||
string content = JsonConvert.SerializeObject(bedrockModel);
|
||||
File.WriteAllText(filepath, content);
|
||||
string texturePath = Path.Combine(Path.GetDirectoryName(filepath), Path.GetFileNameWithoutExtension(filepath)) + ".png";
|
||||
SwapBoxBottomTexture(modelInfo).Save(texturePath, ImageFormat.Png);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ANIM2BOX(SkinANIM anim, Action<SkinBOX> converter)
|
||||
{
|
||||
bool isSlim = anim.GetFlag(SkinAnimFlag.SLIM_MODEL);
|
||||
bool is32x64 = !(anim.GetFlag(SkinAnimFlag.RESOLUTION_64x64) || isSlim);
|
||||
if (!anim.GetFlag(SkinAnimFlag.HEAD_DISABLED))
|
||||
converter(new SkinBOX("HEAD", new Vector3(-4, -8, -4), new Vector3(8), Vector2.Zero));
|
||||
|
||||
if (!is32x64 && !anim.GetFlag(SkinAnimFlag.HEAD_OVERLAY_DISABLED))
|
||||
converter(new SkinBOX("HEADWEAR", new Vector3(-4, -8, -4), new Vector3(8), new Vector2(32, 0)));
|
||||
|
||||
if (!anim.GetFlag(SkinAnimFlag.BODY_DISABLED))
|
||||
converter(new SkinBOX("BODY", new(-4, 0, -2), new(8, 12, 4), new(16, 16)));
|
||||
|
||||
if (!is32x64 && !anim.GetFlag(SkinAnimFlag.BODY_OVERLAY_DISABLED))
|
||||
converter(new SkinBOX("JACKET", new(-4, 0, -2), new(8, 12, 4), new(16, 32)));
|
||||
|
||||
if (!anim.GetFlag(SkinAnimFlag.RIGHT_ARM_DISABLED))
|
||||
converter(new SkinBOX("ARM0", new(isSlim ? -2 : - 3, -2, -2), new(isSlim ? 3 : 4, 12, 4), new(40, 16)));
|
||||
|
||||
if (!is32x64 && !anim.GetFlag(SkinAnimFlag.RIGHT_ARM_OVERLAY_DISABLED))
|
||||
converter(new SkinBOX("SLEEVE0", new(isSlim ? -2 : - 3, -2, -2), new(isSlim ? 3 : 4, 12, 4), new(40, 32)));
|
||||
|
||||
if (!anim.GetFlag(SkinAnimFlag.LEFT_ARM_DISABLED))
|
||||
converter(new SkinBOX("ARM1", new(-1, -2, -2), new(isSlim ? 3 : 4, 12, 4), is32x64 ? new(40, 16) : new(32, 48), mirror: is32x64));
|
||||
|
||||
if (!is32x64 && !anim.GetFlag(SkinAnimFlag.LEFT_ARM_OVERLAY_DISABLED))
|
||||
converter(new SkinBOX("SLEEVE1", new(-1, -2, -2), new(isSlim ? 3 : 4, 12, 4), new(48, 48)));
|
||||
|
||||
if (!anim.GetFlag(SkinAnimFlag.RIGHT_LEG_DISABLED))
|
||||
converter(new SkinBOX("LEG0", new(-2, 0, -2), new(4, 12, 4), new(0, 16)));
|
||||
|
||||
if (!is32x64 && !anim.GetFlag(SkinAnimFlag.RIGHT_LEG_OVERLAY_DISABLED))
|
||||
converter(new SkinBOX("PANTS0", new(-2, 0, -2), new(4, 12, 4), new(0, 32)));
|
||||
|
||||
if (!anim.GetFlag(SkinAnimFlag.LEFT_LEG_DISABLED))
|
||||
{
|
||||
converter(new SkinBOX("LEG1", new(-2, 0, -2), new(4, 12, 4), is32x64 ? new(0, 16) : new(16, 48), mirror: is32x64));
|
||||
}
|
||||
|
||||
if (!is32x64 && !anim.GetFlag(SkinAnimFlag.LEFT_LEG_OVERLAY_DISABLED))
|
||||
{
|
||||
converter(new SkinBOX("PANTS1", new(-2, 0, -2), new(4, 12, 4), new(0, 48)));
|
||||
}
|
||||
}
|
||||
|
||||
private static string TryConvertToSkinBoxType(string name)
|
||||
{
|
||||
if (!SkinBOX.IsValidType(name) && SkinBOX.IsValidType(name.ToUpper()))
|
||||
{
|
||||
return name.ToUpper();
|
||||
}
|
||||
return name.ToLower() switch
|
||||
{
|
||||
"helmet" => "HEAD",
|
||||
"rightarm" => "ARM0",
|
||||
"leftarm" => "ARM1",
|
||||
"rightleg" => "LEG0",
|
||||
"leftleg" => "LEG1",
|
||||
"hat" => "HEADWEAR",
|
||||
"bodyarmor" => "BODY",
|
||||
"rightsleeve" => "SLEEVE0",
|
||||
"leftsleeve" => "SLEEVE1",
|
||||
"rightpants" => "PANTS0",
|
||||
"leftpants" => "PANTS1",
|
||||
_ => name,
|
||||
};
|
||||
}
|
||||
|
||||
private static Vector3 GetOffsetFromOrigin(string boxType, Vector3 origin)
|
||||
{
|
||||
Vector3 partTranslation = GameConstants.GetSkinPartPivot(boxType);
|
||||
Vector3 offset = partTranslation - ((Vector3.UnitY * 24f) - origin);
|
||||
if (offset.X != 0f || offset.Z != 0f)
|
||||
Trace.TraceWarning($"[{nameof(SkinModelImporter)}:{nameof(GetOffsetFromOrigin)}] Warning: skin part({boxType}) offsets only support horizontal offsets.");
|
||||
return offset * Vector3.UnitY;
|
||||
}
|
||||
|
||||
private static Vector3 GetSkinPartPivot(string partName, Vector3 translationUnit)
|
||||
{
|
||||
return TransformSpace(GameConstants.GetSkinPartPivot(partName), Vector3.Zero, translationUnit) + (24f * Vector3.UnitY);
|
||||
}
|
||||
|
||||
private static Vector3 GetOffsetForPart(string offsetType, ref Dictionary<string, SkinPartOffset> offsetLookUp, IEnumerable<SkinPartOffset> partOffsets)
|
||||
{
|
||||
if (offsetLookUp.ContainsKey(offsetType))
|
||||
{
|
||||
return -offsetLookUp[offsetType].Value * Vector3.UnitY;
|
||||
}
|
||||
if (partOffsets.Any(o => o.Type == offsetType))
|
||||
{
|
||||
SkinPartOffset partOffset = partOffsets.First(o => o.Type == offsetType);
|
||||
offsetLookUp.Add(offsetType, partOffset);
|
||||
return -partOffset.Value * Vector3.UnitY;
|
||||
}
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
private static Image SwapBoxBottomTexture(SkinModelInfo modelInfo)
|
||||
{
|
||||
return SwapBoxBottomTexture(modelInfo.Texture, modelInfo.Model.AdditionalBoxes);
|
||||
}
|
||||
|
||||
private static Image SwapBoxBottomTexture(Image texture, IEnumerable<SkinBOX> boxes)
|
||||
{
|
||||
return SwapTextureAreas(texture, boxes.Where(box => !(box.Size == Vector3.One || box.Size == Vector3.Zero)).Select(box =>
|
||||
{
|
||||
var imgPos = Point.Truncate(new PointF(box.UV.X + box.Size.X + box.Size.Z, box.UV.Y));
|
||||
var area = new RectangleF(imgPos, Size.Truncate(new SizeF(box.Size.X, box.Size.Z)));
|
||||
return Rectangle.Truncate(area);
|
||||
}), RotateFlipType.RotateNoneFlipY);
|
||||
}
|
||||
|
||||
private static Image SwapTextureAreas(Image texture, IEnumerable<Rectangle> areasToFix, RotateFlipType type)
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
Trace.TraceError($"[{nameof(SkinModelImporter)}:{nameof(SwapBoxBottomTexture)}] Failed to fix texture: texture is null.");
|
||||
return null;
|
||||
}
|
||||
areasToFix = areasToFix.Where(rect => rect.Size.Width > 0 && rect.Size.Height > 0);
|
||||
Image result = new Bitmap(texture);
|
||||
using var g = Graphics.FromImage(result);
|
||||
g.ApplyConfig(GraphicsConfig.PixelPerfect());
|
||||
foreach (Rectangle area in areasToFix)
|
||||
{
|
||||
Image targetAreaImage = texture.GetArea(area);
|
||||
targetAreaImage.RotateFlip(type);
|
||||
Region clip = g.Clip;
|
||||
g.SetClip(area);
|
||||
g.Clear(Color.Transparent);
|
||||
g.DrawImage(targetAreaImage, area.Location);
|
||||
g.Clip = clip;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Vector3 TranslateToInternalPosition(string boxType, Vector3 origin, Vector3 size, Vector3 translationUnit)
|
||||
{
|
||||
Vector3 pos = TransformSpace(origin, size, translationUnit);
|
||||
// Skin Renderer (and Game) specific offset value.
|
||||
pos.Y += 24f;
|
||||
|
||||
// This will cancel out the part specific translation.
|
||||
Vector3 translation = GameConstants.GetSkinPartTranslation(boxType);
|
||||
pos -= translation;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
private static Vector3 TranslateFromInternalPosistion(SkinBOX skinBox, Vector3 translationUnit)
|
||||
{
|
||||
return TranslateToInternalPosition(skinBox.Type, skinBox.Pos, skinBox.Size, translationUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
PckStudio.ModelSupport/SkinModelInfo.cs
Normal file
24
PckStudio.ModelSupport/SkinModelInfo.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.ModelSupport
|
||||
{
|
||||
public sealed class SkinModelInfo
|
||||
{
|
||||
public SkinModel Model { get; }
|
||||
public SkinANIM Anim { get; }
|
||||
public Image Texture { get; }
|
||||
|
||||
public SkinModelInfo(Image texture, SkinANIM anim, SkinModel model)
|
||||
{
|
||||
Texture = texture;
|
||||
Anim = anim;
|
||||
Model = model;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user