fix for conversion interruption when errors occur
This commit is contained in:
+11
-6
@@ -7,6 +7,7 @@ using dq8chr2glb.Converter;
|
|||||||
using dq8chr2glb.Core.InfoCfg;
|
using dq8chr2glb.Core.InfoCfg;
|
||||||
using dq8chr2glb.Core.MDSFormat;
|
using dq8chr2glb.Core.MDSFormat;
|
||||||
using dq8chr2glb.Core.MOTFormat;
|
using dq8chr2glb.Core.MOTFormat;
|
||||||
|
using dq8chr2glb.Logger;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using Texture = dq8chr2glb.TM2Format.Texture;
|
using Texture = dq8chr2glb.TM2Format.Texture;
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ public class ChrFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
var spacer = new string('.', spaces);
|
var spacer = new string('.', spaces);
|
||||||
Console.WriteLine($" Process: {file.name + spacer} {file.data.Length} bytes");
|
Log.Line($" Process: {file.name + spacer} {file.data.Length} bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
@@ -118,7 +119,16 @@ public class ChrFile
|
|||||||
|
|
||||||
private void ProcessMDSFile(IncludedFile file, string outputDir)
|
private void ProcessMDSFile(IncludedFile file, string outputDir)
|
||||||
{
|
{
|
||||||
|
if (extract)
|
||||||
|
{
|
||||||
|
ProcessRawFile(file, outputDir);
|
||||||
|
}
|
||||||
|
|
||||||
var mdsScene = Reader.Read(file.data, file.name);
|
var mdsScene = Reader.Read(file.data, file.name);
|
||||||
|
if (mdsScene == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (convert)
|
if (convert)
|
||||||
{
|
{
|
||||||
@@ -127,11 +137,6 @@ public class ChrFile
|
|||||||
converter.Convert(mdsScene, textures, rootName);
|
converter.Convert(mdsScene, textures, rootName);
|
||||||
mdsConverters.Add(converter);
|
mdsConverters.Add(converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extract)
|
|
||||||
{
|
|
||||||
ProcessRawFile(file, outputDir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessConfig(IncludedFile file, string outputDir)
|
private void ProcessConfig(IncludedFile file, string outputDir)
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ public class MDSConverter
|
|||||||
var gltfScene = _gltfModel.UseScene("DefaultScene");
|
var gltfScene = _gltfModel.UseScene("DefaultScene");
|
||||||
|
|
||||||
var nodesWithParents = new HashSet<int>();
|
var nodesWithParents = new HashSet<int>();
|
||||||
|
|
||||||
|
if (scene.nodes == null || scene.nodes.Length == 0)
|
||||||
|
{
|
||||||
|
Log.Line($"There is nothing to convert in {name}", LogLevel.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var node in scene.nodes)
|
foreach (var node in scene.nodes)
|
||||||
{
|
{
|
||||||
if (node.parentIndex >= 0)
|
if (node.parentIndex >= 0)
|
||||||
@@ -125,12 +132,13 @@ public class MDSConverter
|
|||||||
|
|
||||||
for (var i = clip.startFrame; i < clip.endFrame; i++)
|
for (var i = clip.startFrame; i < clip.endFrame; i++)
|
||||||
{
|
{
|
||||||
var keyframe = curve.keyframes[i];
|
if (i >= curve.keyframes.Count || curve.keyframes[i] == null)
|
||||||
if (keyframe == null)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var keyframe = curve.keyframes[i];
|
||||||
|
|
||||||
if (curve.curveType == KeyframeType.Quaternion)
|
if (curve.curveType == KeyframeType.Quaternion)
|
||||||
{
|
{
|
||||||
var time = keyframe.frame / FRAMERATE / clip.speed;
|
var time = keyframe.frame / FRAMERATE / clip.speed;
|
||||||
@@ -161,6 +169,11 @@ public class MDSConverter
|
|||||||
{
|
{
|
||||||
foreach (var tex in textures)
|
foreach (var tex in textures)
|
||||||
{
|
{
|
||||||
|
if (tex == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] imageBytes;
|
byte[] imageBytes;
|
||||||
using (var ms = new MemoryStream())
|
using (var ms = new MemoryStream())
|
||||||
{
|
{
|
||||||
@@ -240,20 +253,36 @@ public class MDSConverter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skin.BindJoints(jointBindings);
|
try
|
||||||
|
{
|
||||||
|
skin.BindJoints(jointBindings);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var node in _gltfModel.LogicalNodes)
|
foreach (var node in _gltfModel.LogicalNodes)
|
||||||
{
|
{
|
||||||
if (node.Mesh != null)
|
if (node.Mesh != null)
|
||||||
{
|
{
|
||||||
if (node.Mesh.Primitives.Any(i => i.VertexAccessors.ContainsKey("WEIGHTS_0")))
|
var hasWeights = node.Mesh.Primitives.All(i => i.VertexAccessors.ContainsKey("WEIGHTS_0"));
|
||||||
|
if (hasWeights)
|
||||||
{
|
{
|
||||||
if (Quaternion.Dot(node.LocalTransform.GetDecomposed().Rotation, Quaternion.Identity) >= 0.999f)
|
if (Quaternion.Dot(node.LocalTransform.GetDecomposed().Rotation, Quaternion.Identity) >= 0.999f)
|
||||||
{
|
{
|
||||||
node.LocalTransform = Matrix4x4.Identity;
|
node.LocalTransform = Matrix4x4.Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Skin = skin;
|
try
|
||||||
|
{
|
||||||
|
node.Skin = skin;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Line(e.TargetSite.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,13 +302,20 @@ public class MDSConverter
|
|||||||
{
|
{
|
||||||
var path = Path.Combine(filePath, _name + (textFormat ? ".gltf" : ".glb"));
|
var path = Path.Combine(filePath, _name + (textFormat ? ".gltf" : ".glb"));
|
||||||
|
|
||||||
if (textFormat)
|
try
|
||||||
{
|
{
|
||||||
_gltfModel.SaveGLTF(path);
|
if (textFormat)
|
||||||
|
{
|
||||||
|
_gltfModel.SaveGLTF(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_gltfModel.SaveGLB(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_gltfModel.SaveGLB(path);
|
Log.Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,6 @@ namespace dq8chr2glb.Converter
|
|||||||
var hasUvs = (mdsMesh.features & MeshFeatures.UVs) != 0;
|
var hasUvs = (mdsMesh.features & MeshFeatures.UVs) != 0;
|
||||||
var hasSkinning = (mdsMesh.features & MeshFeatures.Weights) != 0;
|
var hasSkinning = (mdsMesh.features & MeshFeatures.Weights) != 0;
|
||||||
|
|
||||||
// Выбираем подходящий тип вершины на основе атрибутов
|
|
||||||
if (hasSkinning)
|
if (hasSkinning)
|
||||||
{
|
{
|
||||||
if (hasUvs)
|
if (hasUvs)
|
||||||
@@ -52,9 +51,9 @@ namespace dq8chr2glb.Converter
|
|||||||
private static (bool hasUvs, bool hasSkinning) AnalyzeMeshAttributes(MDSMesh mdsMesh)
|
private static (bool hasUvs, bool hasSkinning) AnalyzeMeshAttributes(MDSMesh mdsMesh)
|
||||||
{
|
{
|
||||||
var hasUvs = mdsMesh.uv != null && mdsMesh.uv.Length > 0 &&
|
var hasUvs = mdsMesh.uv != null && mdsMesh.uv.Length > 0 &&
|
||||||
Array.Exists(mdsMesh.uv, uv => uv != null && uv.Length >= 2);
|
Array.Exists(mdsMesh.uv, uv => uv != null && uv.Length >= 2);
|
||||||
var hasSkinning = mdsMesh.weights != null && mdsMesh.bones != null &&
|
var hasSkinning = mdsMesh.weights != null && mdsMesh.bones != null &&
|
||||||
mdsMesh.bones.Length > 0; // && Array.Exists(mdsMesh.bones, b => b != null)
|
mdsMesh.bones.Length > 0;
|
||||||
|
|
||||||
return (hasUvs, hasSkinning);
|
return (hasUvs, hasSkinning);
|
||||||
}
|
}
|
||||||
@@ -102,6 +101,8 @@ namespace dq8chr2glb.Converter
|
|||||||
var boneWeights = mdsMesh.weights[i];
|
var boneWeights = mdsMesh.weights[i];
|
||||||
|
|
||||||
var bindings = new (int JointIndex, float Weight)[Math.Min(4, boneIndices.Length)];
|
var bindings = new (int JointIndex, float Weight)[Math.Min(4, boneIndices.Length)];
|
||||||
|
|
||||||
|
var wSum = 0f;
|
||||||
for (var b = 0; b < bindings.Length; b++)
|
for (var b = 0; b < bindings.Length; b++)
|
||||||
{
|
{
|
||||||
var mdsBoneIndex = boneIndices[b];
|
var mdsBoneIndex = boneIndices[b];
|
||||||
@@ -109,14 +110,25 @@ namespace dq8chr2glb.Converter
|
|||||||
{
|
{
|
||||||
var gltfBoneIndex = nodesMap[mdsBoneIndex];
|
var gltfBoneIndex = nodesMap[mdsBoneIndex];
|
||||||
bindings[b] = (Math.Max(gltfBoneIndex, 0), boneWeights[b]);
|
bindings[b] = (Math.Max(gltfBoneIndex, 0), boneWeights[b]);
|
||||||
|
wSum += boneWeights[b];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Обработка случая, когда MDS-индекс не найден в маппинге
|
bindings[b] = (0, boneWeights[b]);
|
||||||
bindings[b] = (0, boneWeights[b]); // или другое значение по умолчанию
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wSum < 0.00001f)
|
||||||
|
{
|
||||||
|
skinningData.Add(Array.Empty<(int, float)>());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index = 0; index < bindings.Length; index++)
|
||||||
|
{
|
||||||
|
bindings[index].Weight /= wSum;
|
||||||
|
}
|
||||||
|
|
||||||
skinningData.Add(bindings);
|
skinningData.Add(bindings);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -185,7 +197,16 @@ namespace dq8chr2glb.Converter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return root.CreateMesh(meshBuilder);
|
try
|
||||||
|
{
|
||||||
|
return root.CreateMesh(meshBuilder);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Line($"Create Mesh Error: {mdsMesh.name}", LogLevel.Error);
|
||||||
|
Log.Error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VertexBuilder<TvG, TvM, TvS> CreateVertex<TvG, TvM, TvS>(
|
private static VertexBuilder<TvG, TvM, TvS> CreateVertex<TvG, TvM, TvS>(
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class Reader
|
|||||||
|
|
||||||
if (header.version != 200)
|
if (header.version != 200)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Unknown version: {header.version}");
|
Log.Line($"Unknown version: {header.version}.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using dq8chr2glb.Logger;
|
||||||
|
|
||||||
namespace dq8chr2glb.Core.MOTFormat;
|
namespace dq8chr2glb.Core.MOTFormat;
|
||||||
|
|
||||||
@@ -93,7 +94,15 @@ public class Importer
|
|||||||
keyframe.type = KeyframeType.Quaternion;
|
keyframe.type = KeyframeType.Quaternion;
|
||||||
keyframe.rotation = ReadQuaternion(data, valuesOffset, scale);
|
keyframe.rotation = ReadQuaternion(data, valuesOffset, scale);
|
||||||
|
|
||||||
curve.keyframes[frame] = keyframe;
|
try
|
||||||
|
{
|
||||||
|
curve.keyframes[frame] = keyframe;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Line($"Skip negative frame number: {frame}", LogLevel.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
valuesOffset += 8;
|
valuesOffset += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,6 +153,11 @@ public class Importer
|
|||||||
var toValues = header.valuesOffset;
|
var toValues = header.valuesOffset;
|
||||||
foreach (var frame in frames)
|
foreach (var frame in frames)
|
||||||
{
|
{
|
||||||
|
if (frame == null || frame < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var scale = new Vector3(header.scaleX, header.scaleY,
|
var scale = new Vector3(header.scaleX, header.scaleY,
|
||||||
header.scaleZ);
|
header.scaleZ);
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,38 @@ public enum LogLevel
|
|||||||
|
|
||||||
public static class Log
|
public static class Log
|
||||||
{
|
{
|
||||||
|
// public const LogLevel LogLevel = Logger.LogLevel.Info;
|
||||||
|
|
||||||
public static void Line(object data, LogLevel level = LogLevel.Debug)
|
public static void Line(object data, LogLevel level = LogLevel.Debug)
|
||||||
{
|
{
|
||||||
|
var lastColor = Console.ForegroundColor;
|
||||||
|
var color = level switch
|
||||||
|
{
|
||||||
|
LogLevel.Error => ConsoleColor.Red,
|
||||||
|
LogLevel.Debug => ConsoleColor.White,
|
||||||
|
LogLevel.Info => ConsoleColor.White,
|
||||||
|
LogLevel.Warning => ConsoleColor.Yellow,
|
||||||
|
LogLevel.None => ConsoleColor.White,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (level == LogLevel.Debug)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.ForegroundColor = color;
|
||||||
Console.WriteLine(data.ToString());
|
Console.WriteLine(data.ToString());
|
||||||
|
Console.ForegroundColor = lastColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Error(Exception e, LogLevel level = LogLevel.Error)
|
public static void Error(Exception e, LogLevel level = LogLevel.Error)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{e.Message}\n From:\n{e.Source}\nStacktrace:\n{e.StackTrace}");
|
var lastColor = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Write($"[{level}] ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkRed;
|
||||||
|
// Console.Write($"{e.Message}\n From:\n{e.Source}\nStacktrace:\n{e.StackTrace}\n");
|
||||||
|
Console.Write($"{e.Message}\n");
|
||||||
|
Console.ForegroundColor = lastColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using dq8chr2glb.Logger;
|
||||||
|
|
||||||
namespace dq8chr2glb;
|
namespace dq8chr2glb;
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ public class Program
|
|||||||
var files = Directory.GetFiles(inputPath, "*.chr", SearchOption.TopDirectoryOnly);
|
var files = Directory.GetFiles(inputPath, "*.chr", SearchOption.TopDirectoryOnly);
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Processing: {Path.GetFileName(file)}");
|
Log.Line($"Processing: {Path.GetFileName(file)}", LogLevel.Info);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
chrFile.Process(file, outputPath);
|
chrFile.Process(file, outputPath);
|
||||||
@@ -78,18 +79,18 @@ public class Program
|
|||||||
chrFile.Clean();
|
chrFile.Clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(files.Length == 0 ? "No .chr files found in the input directory." : "Done!");
|
Log.Line(files.Length == 0 ? "No .chr files found in the input directory." : "Done!", LogLevel.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!File.Exists(inputPath))
|
if (!File.Exists(inputPath))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Error: Input file not found: {inputPath}");
|
Log.Line($"Input file not found: {inputPath}", LogLevel.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chrFile.Process(inputPath, outputPath);
|
chrFile.Process(inputPath, outputPath);
|
||||||
Console.WriteLine("Done!");
|
Log.Line("Done!", LogLevel.Info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using dq8chr2glb.Logger;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
@@ -12,6 +13,12 @@ public static class TM2Format
|
|||||||
var header = TM2Header.GetHeader(data);
|
var header = TM2Header.GetHeader(data);
|
||||||
var image = GetImageBuffer(header, data);
|
var image = GetImageBuffer(header, data);
|
||||||
|
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
Log.Line($"Failed to create texture {name}", LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var img = new Image<Rgba32>(header.Width, header.Height);
|
var img = new Image<Rgba32>(header.Width, header.Height);
|
||||||
|
|
||||||
for (var y = 0; y < header.Height; y++)
|
for (var y = 0; y < header.Height; y++)
|
||||||
@@ -45,15 +52,23 @@ public static class TM2Format
|
|||||||
{
|
{
|
||||||
var palette = new byte[256][];
|
var palette = new byte[256][];
|
||||||
|
|
||||||
for (var i = 0; i < 256; ++i)
|
try
|
||||||
{
|
{
|
||||||
var rgba = new byte[4];
|
for (var i = 0; i < 256; ++i)
|
||||||
Array.Copy(file, 0x40 + header.ImageSize + i * 4, rgba, 0, 4);
|
{
|
||||||
rgba[3] = (byte)Math.Min(rgba[3] << 1, 0xFF);
|
var rgba = new byte[4];
|
||||||
|
Array.Copy(file, 0x40 + header.ImageSize + i * 4, rgba, 0, 4);
|
||||||
|
rgba[3] = (byte)Math.Min(rgba[3] << 1, 0xFF);
|
||||||
|
|
||||||
(rgba[2], rgba[0]) = (rgba[0], rgba[2]);
|
(rgba[2], rgba[0]) = (rgba[0], rgba[2]);
|
||||||
|
|
||||||
palette[(i & 0xe7) | ((i & 0x10) >> 1) | ((i & 0x08) << 1)] = rgba;
|
palette[(i & 0xe7) | ((i & 0x10) >> 1) | ((i & 0x08) << 1)] = rgba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
palette = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette;
|
return palette;
|
||||||
@@ -63,6 +78,11 @@ public static class TM2Format
|
|||||||
{
|
{
|
||||||
var palette = GetPalette8bbp(tm2file, header);
|
var palette = GetPalette8bbp(tm2file, header);
|
||||||
|
|
||||||
|
if (palette == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var imageBuf = new byte[4 * header.Width * header.Height];
|
var imageBuf = new byte[4 * header.Width * header.Height];
|
||||||
for (var y = header.Height - 1; y >= 0; --y)
|
for (var y = header.Height - 1; y >= 0; --y)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user