animations and QOL

This commit is contained in:
bird_egop
2026-06-12 02:06:32 +03:00
parent 4d0d1aca12
commit 1cb3940cce
14 changed files with 444 additions and 32 deletions
@@ -60,6 +60,9 @@ public static class MshMeshDocumentImporter
var indices = Msh0x06.ReadComponent(fs, archive);
var batches = Msh0x0D.ReadComponent(fs, archive);
var animationDescriptors = Msh0x08.ReadComponent(fs, archive);
var animationMapComponent = TryReadAnimationMap(fs, archive, warnings);
var animationMap = animationMapComponent?.Entries ?? [];
var maxAnimationTime = animationMapComponent?.MaxAnimationTime ?? InferMaxAnimationTime(animationDescriptors);
var restPoses = MshRestPoseBuilder.BuildRestPose(nodes, animationDescriptors);
var names = TryReadNames(fs, archive, warnings);
@@ -121,7 +124,10 @@ public static class MshMeshDocumentImporter
Normals = normals,
Uvs = uvs,
Indices = indices,
Batches = batches
Batches = batches,
TransformKeyframes = animationDescriptors,
AnimationMap = animationMap,
MaxAnimationTime = maxAnimationTime
}
};
}
@@ -228,6 +234,27 @@ public static class MshMeshDocumentImporter
}
}
private static Msh0x13Component? TryReadAnimationMap(FileStream fs, NResArchive archive, ICollection<string> warnings)
{
try
{
return Msh0x13.ReadComponent(fs, archive);
}
catch (Exception ex)
{
warnings.Add($"MSH 0x13 animation map is unavailable: {ex.Message}");
return null;
}
}
private static int InferMaxAnimationTime(IReadOnlyList<MshTransformKeyframe> keyframes)
{
if (keyframes.Count == 0)
return 0;
return (int)MathF.Ceiling(MathF.Max(0.0f, keyframes.Max(x => x.Time)));
}
private static string ResolvePieceName(IReadOnlyList<string> names, int nodeIndex)
{
if (nodeIndex >= 0 && nodeIndex < names.Count && !string.IsNullOrWhiteSpace(names[nodeIndex]))
@@ -4,6 +4,7 @@ using NResUI.Rendering.Inspection;
using NResUI.Rendering.Materials;
using NResUI.Rendering.Viewport;
using NResUI.Rendering.Viewport.Meshes;
using NResUI.Rendering.Viewport.Msh;
using Silk.NET.OpenGL;
namespace NResUI.Rendering.Import.Msh;
@@ -26,18 +27,25 @@ public static class MshViewportMeshFactory
var result = new List<ViewportPiece>();
var modelGeometry = document.MshModelGeometry;
var mshToViewportTransform = Matrix4x4.CreateRotationX(-MathF.PI * 0.5f);
var poses = MshRestPoseBuilder.BuildPose(
modelGeometry.Nodes,
modelGeometry.TransformKeyframes,
modelGeometry.AnimationMap,
renderState.AnimationPoseMode,
renderState.AnimationSampleTime);
foreach (var pieceInfo in document.Pieces)
{
if (!renderState.IsPieceVisible(pieceInfo.Id))
continue;
var pieceTransform = poses[pieceInfo.Id].MeshSpaceTransform;
var (pieceState, pieceLod) = ResolvePieceStateLod(document, pieceInfo, renderState);
var slotIndex = pieceInfo.ResolveSlotIndex(pieceState, pieceLod);
if (slotIndex == ushort.MaxValue || slotIndex >= modelGeometry.GeometrySlots.Slots.Count)
{
if (renderState.ShowEmptyPieces)
AddEmptyPieceMarker(gl, result, pieceInfo, mshToViewportTransform, "No geometry slot for selected state/LOD");
AddEmptyPieceMarker(gl, result, pieceInfo, pieceTransform, mshToViewportTransform, "No geometry slot for selected state/LOD");
continue;
}
@@ -45,7 +53,7 @@ public static class MshViewportMeshFactory
if (!slot.HasBatches)
{
if (renderState.ShowEmptyPieces)
AddEmptyPieceMarker(gl, result, pieceInfo, mshToViewportTransform, "Geometry slot has no batches", slotIndex);
AddEmptyPieceMarker(gl, result, pieceInfo, pieceTransform, mshToViewportTransform, "Geometry slot has no batches", slotIndex);
continue;
}
@@ -60,7 +68,7 @@ public static class MshViewportMeshFactory
if (meshBuildResult == null)
{
if (renderState.ShowEmptyPieces)
AddEmptyPieceMarker(gl, result, pieceInfo, mshToViewportTransform, "Geometry slot produced no valid triangles", slotIndex);
AddEmptyPieceMarker(gl, result, pieceInfo, pieceTransform, mshToViewportTransform, "Geometry slot produced no valid triangles", slotIndex);
continue;
}
@@ -68,7 +76,7 @@ public static class MshViewportMeshFactory
id: pieceInfo.Id,
name: pieceInfo.Name,
meshes: meshBuildResult.Meshes,
localTransform: pieceInfo.MeshSpaceTransform * mshToViewportTransform,
localTransform: pieceTransform * mshToViewportTransform,
boundsMin: meshBuildResult.BoundsMin,
boundsMax: meshBuildResult.BoundsMax,
debugInfo: new ViewportPieceDebugInfo
@@ -121,6 +129,7 @@ public static class MshViewportMeshFactory
GL gl,
List<ViewportPiece> pieces,
MeshPieceInfo pieceInfo,
Matrix4x4 pieceTransform,
Matrix4x4 mshToViewportTransform,
string reason,
int geometrySlotIndex = -1)
@@ -131,7 +140,7 @@ public static class MshViewportMeshFactory
id: pieceInfo.Id,
name: pieceInfo.Name,
mesh: marker,
localTransform: pieceInfo.MeshSpaceTransform * mshToViewportTransform,
localTransform: pieceTransform * mshToViewportTransform,
boundsMin: new Vector3(-0.35f, -0.35f, -0.35f),
boundsMax: new Vector3(0.35f, 0.35f, 0.35f),
debugInfo: new ViewportPieceDebugInfo