socket backend

This commit is contained in:
4sval 2023-01-09 13:11:35 +01:00
parent ac0e11ea39
commit bf171b28cd
7 changed files with 139 additions and 63 deletions

@ -1 +1 @@
Subproject commit d6bd6e58cd084ab5fadd46a433bfbe95316e4890
Subproject commit 581d29b275a2581c2b246991fe8e5f988605e0b5

View File

@ -18,13 +18,15 @@ public class Skeleton : IDisposable
public readonly Dictionary<int, Transform> BonesTransformByIndex;
public readonly bool IsLoaded;
public readonly Socket[] Sockets;
public Animation Anim;
private FVector _previousMatrix;
public Skeleton()
{
BonesIndexByName = new Dictionary<string, int>();
BonesTransformByIndex = new Dictionary<int, Transform>();
}
public Skeleton(FPackageIndex package, Transform transform)
public Skeleton(FPackageIndex package, Transform transform) : this()
{
UnrealSkeleton = package.Load<USkeleton>();
if (UnrealSkeleton == null) return;
@ -64,22 +66,6 @@ public class Skeleton : IDisposable
transforms.Clear();
}
IsLoaded = true;
Sockets = new Socket[UnrealSkeleton.Sockets.Length];
for (int i = 0; i < Sockets.Length; i++)
{
if (UnrealSkeleton.Sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
if (!BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
!BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform))
{
Sockets[i] = new Socket(socket);
}
else
{
Sockets[i] = new Socket(socket, boneTransform);
}
}
}
public void SetAnimation(CAnimSet anim)
@ -87,20 +73,13 @@ public class Skeleton : IDisposable
Anim = new Animation(anim, BonesIndexByName, BonesTransformByIndex);
}
public void UpdateSocketsMatrix(Transform t)
public void UpdateRootBoneMatrix(Matrix4x4 delta)
{
var m = t.Position;
if (m == _previousMatrix) return;
// Matrix4x4.Decompose(delta, out var scale, out var rotation, out var position);
// Log.Logger.Information("Update");
var delta = _previousMatrix - m;
Log.Logger.Information("Update {0}", delta);
// BonesTransformByIndex[0].Relation.Translation += delta;
foreach (var socket in Sockets)
{
socket.Transform.Relation.Translation += delta;
}
_previousMatrix = m;
// TODO: support for rotation and scale
BonesTransformByIndex[0].Relation.Translation += delta.Translation;
}
public void SetUniform(Shader shader)

View File

@ -52,8 +52,12 @@ public class Model : IDisposable
public bool HasSkeleton => Skeleton is { IsLoaded: true };
public readonly Skeleton Skeleton;
public bool HasSockets => Sockets.Length > 0;
public readonly Socket[] Sockets;
public int TransformsCount;
public readonly List<Transform> Transforms;
private Matrix4x4 _previousMatrix;
public readonly Morph[] Morphs;
@ -76,14 +80,40 @@ public class Model : IDisposable
}
public Model(UStaticMesh export, CStaticMesh staticMesh) : this(export, staticMesh, Transform.Identity) {}
public Model(UStaticMesh export, CStaticMesh staticMesh, Transform transform) : this(export, export.Materials, null, staticMesh.LODs.Count, staticMesh.LODs[0], staticMesh.LODs[0].Verts, transform)
{
Box = staticMesh.BoundingBox * Constants.SCALE_DOWN_RATIO;
Sockets = new Socket[export.Sockets.Length];
for (int i = 0; i < Sockets.Length; i++)
{
if (export.Sockets[i].Load<UStaticMeshSocket>() is not { } socket) continue;
Sockets[i] = new Socket(socket);
}
}
private Model(USkeletalMesh export, CSkeletalMesh skeletalMesh, Transform transform) : this(export, export.Materials, export.Skeleton, skeletalMesh.LODs.Count, skeletalMesh.LODs[0], skeletalMesh.LODs[0].Verts, transform)
{
Box = skeletalMesh.BoundingBox * Constants.SCALE_DOWN_RATIO;
var sockets = new List<FPackageIndex>();
sockets.AddRange(export.Sockets);
if (HasSkeleton) sockets.AddRange(Skeleton.UnrealSkeleton.Sockets);
Sockets = new Socket[sockets.Count];
for (int i = 0; i < Sockets.Length; i++)
{
if (sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
if (!Skeleton.BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
!Skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform))
{
Sockets[i] = new Socket(socket);
}
else
{
Sockets[i] = new Socket(socket, boneTransform);
}
}
}
public Model(USkeletalMesh export, CSkeletalMesh skeletalMesh) : this(export, skeletalMesh, Transform.Identity)
{
@ -194,20 +224,44 @@ public class Model : IDisposable
{
TransformsCount++;
Transforms.Add(transform);
_previousMatrix = transform.Matrix;
}
public void UpdateMatrix() => UpdateMatrix(SelectedInstance);
public void UpdateMatrix(Transform transform) => UpdateMatrix(SelectedInstance, transform);
public void UpdateMatrix(int instance, Transform transform)
public void UpdateMatrices(Options options)
{
Transforms[instance] = transform;
UpdateMatrix(instance);
UpdateMatrices();
if (!HasSkeleton)
return;
for (int s = 0; s < Sockets.Length; s++)
{
for (int g = 0; g < Sockets[s].AttachedModels.Count; g++)
{
if (!options.TryGetModel(Sockets[s].AttachedModels[g], out var attachedModel))
continue;
attachedModel.Transforms[attachedModel.SelectedInstance].Relation = Sockets[s].Transform.Matrix;
attachedModel.UpdateMatrices();
}
}
}
public void UpdateMatrix(int instance)
private void UpdateMatrices()
{
var matrix = Transforms[SelectedInstance].Matrix;
if (matrix == _previousMatrix) return;
_matrixVbo.Bind();
_matrixVbo.Update(instance, Transforms[instance].Matrix);
_matrixVbo.Update(SelectedInstance, matrix);
_matrixVbo.Unbind();
var delta = matrix - _previousMatrix;
foreach (var socket in Sockets)
{
socket.UpdateSocketMatrix(delta);
}
if (HasSkeleton) Skeleton.UpdateRootBoneMatrix(delta);
_previousMatrix = matrix;
}
public void UpdateMorph(int index)

View File

@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
using CUE4Parse.UE4.Objects.Core.Misc;
namespace FModel.Views.Snooper.Models;
@ -10,29 +13,48 @@ public class Socket : IDisposable
public readonly string Bone;
public readonly Transform Transform;
public FGuid AttachedModel;
public readonly List<FGuid> AttachedModels;
public Socket(USkeletalMeshSocket socket)
private Socket()
{
Bone = "None";
Transform = Transform.Identity;
AttachedModels = new List<FGuid>();
}
public Socket(UStaticMeshSocket socket) : this()
{
Name = socket.SocketName.Text;
Bone = socket.BoneName.Text;
Transform = Transform.Identity;
Transform.Rotation = socket.RelativeRotation.Quaternion();
Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO;
Transform.Scale = socket.RelativeScale;
}
public Socket(USkeletalMeshSocket socket, Transform transform)
public Socket(USkeletalMeshSocket socket) : this()
{
Name = socket.SocketName.Text;
Bone = socket.BoneName.Text;
Transform.Rotation = socket.RelativeRotation.Quaternion();
Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO;
Transform.Scale = socket.RelativeScale;
}
public Socket(USkeletalMeshSocket socket, Transform transform) : this()
{
Name = socket.SocketName.Text;
Bone = socket.BoneName.Text;
Transform = Transform.Identity;
Transform.Relation = transform.Matrix;
Transform.Rotation = socket.RelativeRotation.Quaternion();
Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO;
Transform.Scale = socket.RelativeScale;
}
public void UpdateSocketMatrix(Matrix4x4 delta)
{
// TODO: support for rotation and scale
Transform.Relation.Translation += delta.Translation;
}
public void Dispose()
{
throw new NotImplementedException();

View File

@ -124,6 +124,7 @@ public class Renderer : IDisposable
// render model pass
foreach (var model in Options.Models.Values)
{
model.UpdateMatrices(Options);
if (!model.Show) continue;
model.Render(_shader);
}

View File

@ -394,27 +394,33 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
private void DrawSockets(Snooper s)
{
var selectedGuid = s.Renderer.Options.SelectedModel;
if (!s.Renderer.Options.TryGetModel(out var selectedModel))
return;
foreach (var model in s.Renderer.Options.Models.Values)
{
if (!model.HasSkeleton || model.IsSelected) return;
if (ImGui.TreeNode($"{model.Name} [{model.Skeleton.Sockets.Length}]"))
if (!model.HasSockets || model.IsSelected) continue;
if (ImGui.TreeNode($"{model.Name} [{model.Sockets.Length}]"))
{
var i = 0;
foreach (var socket in model.Skeleton.Sockets)
foreach (var socket in model.Sockets)
{
ImGui.PushID(i);
ImGui.Text($"'{socket.Name}' attached to '{socket.Bone}'");
ImGui.Text($"P: {socket.Transform.Matrix.Translation.X} | {socket.Transform.Matrix.Translation.Y} | {socket.Transform.Matrix.Translation.Z}");
if (ImGui.Button("Attach") && s.Renderer.Options.TryGetModel(out var selected))
switch (socket.AttachedModels.Contains(selectedGuid))
{
socket.AttachedModel = s.Renderer.Options.SelectedModel;
selected.UpdateMatrix(new Transform
{
Relation = socket.Transform.Matrix,
Position = FVector.ZeroVector,
Rotation = new FQuat(0),
Scale = FVector.OneVector
});
case false when ImGui.Button($"Attach to '{socket.Name}'"):
socket.AttachedModels.Add(selectedGuid);
// reset PRS to 0 so it's attached to the actual position (can be transformed relative to the socket later by the user)
selectedModel.Transforms[selectedModel.SelectedInstance].Position = FVector.ZeroVector;
selectedModel.Transforms[selectedModel.SelectedInstance].Rotation = FQuat.Identity;
selectedModel.Transforms[selectedModel.SelectedInstance].Scale = FVector.OneVector;
break;
case true when ImGui.Button($"Detach from '{socket.Name}'"):
socket.AttachedModels.Remove(selectedGuid);
// reset PRS relation to O
selectedModel.Transforms[selectedModel.SelectedInstance].Relation = Matrix4x4.Identity;
break;
}
ImGui.PopID();
i++;
@ -422,6 +428,20 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.TreePop();
}
}
// if (ImGui.BeginTable("socket_editor", 2))
// {
// Layout("Models");
// ImGui.PushID(1);
// ImGui.Combo("", ref m, "Fly Cam\0Arcball\0");
// ImGui.PopID();
//
// Layout("Attach To");ImGui.PushID(2);
// ImGui.Combo("world_mode", ref m, "Fly Cam\0Arcball\0");
// ImGui.PopID();
//
// ImGui.EndTable();
// }
}
private void DrawDetails(Snooper s)
@ -437,12 +457,12 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
{
Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}");
Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}");
Layout("Sockets");ImGui.Text($" : x{model.Skeleton.Sockets.Length}");
}
else
{
Layout("Two Sided");ImGui.Text($" : {model.TwoSided}");
}
Layout("Sockets");ImGui.Text($" : x{model.Sockets.Length}");
ImGui.EndTable();
}
@ -532,7 +552,6 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
ImGui.EndDisabled(); ImGui.PopID();
model.Transforms[model.SelectedInstance].ImGuiTransform(s.Renderer.CameraOp.Speed / 100f);
model.UpdateMatrix();
ImGui.EndTabItem();
}

View File

@ -59,6 +59,7 @@ public class Transform
ImGui.TreePop();
}
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
if (ImGui.TreeNode("Scale"))
{
ImGui.SetNextItemWidth(width);