fix for conversion interruption when errors occur

This commit is contained in:
Boris Nikolaev
2026-02-14 13:31:12 +03:00
parent 92b615f077
commit 67b2837684
8 changed files with 158 additions and 36 deletions
+11 -6
View File
@@ -7,6 +7,7 @@ using dq8chr2glb.Converter;
using dq8chr2glb.Core.InfoCfg;
using dq8chr2glb.Core.MDSFormat;
using dq8chr2glb.Core.MOTFormat;
using dq8chr2glb.Logger;
using SixLabors.ImageSharp;
using Texture = dq8chr2glb.TM2Format.Texture;
@@ -80,7 +81,7 @@ public class ChrFile
}
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()
@@ -118,7 +119,16 @@ public class ChrFile
private void ProcessMDSFile(IncludedFile file, string outputDir)
{
if (extract)
{
ProcessRawFile(file, outputDir);
}
var mdsScene = Reader.Read(file.data, file.name);
if (mdsScene == null)
{
return;
}
if (convert)
{
@@ -127,11 +137,6 @@ public class ChrFile
converter.Convert(mdsScene, textures, rootName);
mdsConverters.Add(converter);
}
if (extract)
{
ProcessRawFile(file, outputDir);
}
}
private void ProcessConfig(IncludedFile file, string outputDir)
+46 -10
View File
@@ -38,6 +38,13 @@ public class MDSConverter
var gltfScene = _gltfModel.UseScene("DefaultScene");
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)
{
if (node.parentIndex >= 0)
@@ -125,12 +132,13 @@ public class MDSConverter
for (var i = clip.startFrame; i < clip.endFrame; i++)
{
var keyframe = curve.keyframes[i];
if (keyframe == null)
if (i >= curve.keyframes.Count || curve.keyframes[i] == null)
{
continue;
}
var keyframe = curve.keyframes[i];
if (curve.curveType == KeyframeType.Quaternion)
{
var time = keyframe.frame / FRAMERATE / clip.speed;
@@ -161,6 +169,11 @@ public class MDSConverter
{
foreach (var tex in textures)
{
if (tex == null)
{
continue;
}
byte[] imageBytes;
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)
{
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)
{
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"));
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);
}
}
}
}
+27 -6
View File
@@ -20,7 +20,6 @@ namespace dq8chr2glb.Converter
var hasUvs = (mdsMesh.features & MeshFeatures.UVs) != 0;
var hasSkinning = (mdsMesh.features & MeshFeatures.Weights) != 0;
// Выбираем подходящий тип вершины на основе атрибутов
if (hasSkinning)
{
if (hasUvs)
@@ -52,9 +51,9 @@ namespace dq8chr2glb.Converter
private static (bool hasUvs, bool hasSkinning) AnalyzeMeshAttributes(MDSMesh mdsMesh)
{
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 &&
mdsMesh.bones.Length > 0; // && Array.Exists(mdsMesh.bones, b => b != null)
mdsMesh.bones.Length > 0;
return (hasUvs, hasSkinning);
}
@@ -102,6 +101,8 @@ namespace dq8chr2glb.Converter
var boneWeights = mdsMesh.weights[i];
var bindings = new (int JointIndex, float Weight)[Math.Min(4, boneIndices.Length)];
var wSum = 0f;
for (var b = 0; b < bindings.Length; b++)
{
var mdsBoneIndex = boneIndices[b];
@@ -109,14 +110,25 @@ namespace dq8chr2glb.Converter
{
var gltfBoneIndex = nodesMap[mdsBoneIndex];
bindings[b] = (Math.Max(gltfBoneIndex, 0), boneWeights[b]);
wSum += boneWeights[b];
}
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);
}
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>(
+1 -1
View File
@@ -17,7 +17,7 @@ public class Reader
if (header.version != 200)
{
Console.WriteLine($"Unknown version: {header.version}");
Log.Line($"Unknown version: {header.version}.");
return null;
}
+15 -1
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using dq8chr2glb.Logger;
namespace dq8chr2glb.Core.MOTFormat;
@@ -93,7 +94,15 @@ public class Importer
keyframe.type = KeyframeType.Quaternion;
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;
}
}
@@ -144,6 +153,11 @@ public class Importer
var toValues = header.valuesOffset;
foreach (var frame in frames)
{
if (frame == null || frame < 0)
{
continue;
}
var scale = new Vector3(header.scaleX, header.scaleY,
header.scaleZ);
+27 -2
View File
@@ -13,13 +13,38 @@ public enum LogLevel
public static class Log
{
// public const LogLevel LogLevel = Logger.LogLevel.Info;
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.ForegroundColor = lastColor;
}
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;
}
}
+5 -4
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using dq8chr2glb.Logger;
namespace dq8chr2glb;
@@ -64,7 +65,7 @@ public class Program
var files = Directory.GetFiles(inputPath, "*.chr", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
Console.WriteLine($"Processing: {Path.GetFileName(file)}");
Log.Line($"Processing: {Path.GetFileName(file)}", LogLevel.Info);
try
{
chrFile.Process(file, outputPath);
@@ -78,18 +79,18 @@ public class Program
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
{
if (!File.Exists(inputPath))
{
Console.WriteLine($"Error: Input file not found: {inputPath}");
Log.Line($"Input file not found: {inputPath}", LogLevel.Error);
return;
}
chrFile.Process(inputPath, outputPath);
Console.WriteLine("Done!");
Log.Line("Done!", LogLevel.Info);
}
}
+26 -6
View File
@@ -1,4 +1,5 @@
using System;
using dq8chr2glb.Logger;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
@@ -12,6 +13,12 @@ public static class TM2Format
var header = TM2Header.GetHeader(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);
for (var y = 0; y < header.Height; y++)
@@ -45,15 +52,23 @@ public static class TM2Format
{
var palette = new byte[256][];
for (var i = 0; i < 256; ++i)
try
{
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);
for (var i = 0; i < 256; ++i)
{
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;
@@ -63,6 +78,11 @@ public static class TM2Format
{
var palette = GetPalette8bbp(tm2file, header);
if (palette == null)
{
return null;
}
var imageBuf = new byte[4 * header.Width * header.Height];
for (var y = header.Height - 1; y >= 0; --y)
{