mirror of
https://github.com/4sval/FModel.git
synced 2026-04-01 14:45:45 -05:00
socket backend
This commit is contained in:
parent
ac0e11ea39
commit
bf171b28cd
|
|
@ -1 +1 @@
|
|||
Subproject commit d6bd6e58cd084ab5fadd46a433bfbe95316e4890
|
||||
Subproject commit 581d29b275a2581c2b246991fe8e5f988605e0b5
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public class Transform
|
|||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Scale"))
|
||||
{
|
||||
ImGui.SetNextItemWidth(width);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user