mirror of
https://github.com/4sval/FModel.git
synced 2026-04-21 17:17:49 -05:00
some animations work, others do not, awaiting improvements
This commit is contained in:
parent
bbda1c5c0d
commit
f36a7b79cd
|
|
@ -1 +1 @@
|
|||
Subproject commit eef05377f72ec488588b9c2b7dcc26bd9dea7c71
|
||||
Subproject commit 3a7ddc13bb370e5988bc122a7cf67ad8036ccaf1
|
||||
|
|
@ -11,6 +11,7 @@ public static class Constants
|
|||
|
||||
public const float SCALE_DOWN_RATIO = 0.01F;
|
||||
public const int SAMPLES_COUNT = 4;
|
||||
public const int MAX_BONE_UNIFORM = 250;
|
||||
|
||||
public const string WHITE = "#DAE5F2";
|
||||
public const string GRAY = "#BBBBBB";
|
||||
|
|
|
|||
|
|
@ -12,12 +12,10 @@ layout (location = 9) in mat4 vInstanceMatrix;
|
|||
layout (location = 13) in vec3 vMorphTargetPos;
|
||||
layout (location = 14) in vec3 vMorphTargetTangent;
|
||||
|
||||
//const int MAX_BONES = 140;
|
||||
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uProjection;
|
||||
uniform float uMorphTime;
|
||||
//uniform mat4 uFinalBonesMatrix[MAX_BONES];
|
||||
uniform mat4 uFinalBonesMatrix[250];
|
||||
|
||||
out vec3 fPos;
|
||||
out vec3 fNormal;
|
||||
|
|
@ -32,25 +30,27 @@ void main()
|
|||
vec4 bindNormal = vec4(vNormal, 1.0);
|
||||
vec4 bindTangent = vec4(mix(vTangent, vMorphTargetTangent, uMorphTime), 1.0);
|
||||
|
||||
// vec4 finalPos = vec4(0.0);
|
||||
// vec4 finalNormal = vec4(0.0);
|
||||
// vec4 finalTangent = vec4(0.0);
|
||||
// vec4 weights = normalize(vBoneWeights);
|
||||
// for(int i = 0 ; i < 4; i++)
|
||||
// {
|
||||
// int boneIndex = int(vBoneIds[i]);
|
||||
// if(boneIndex < 0) break;
|
||||
//
|
||||
// finalPos += uFinalBonesMatrix[boneIndex] * bindPos * weights[i];
|
||||
// finalNormal += uFinalBonesMatrix[boneIndex] * bindNormal * weights[i];
|
||||
// finalTangent += uFinalBonesMatrix[boneIndex] * bindTangent * weights[i];
|
||||
// }
|
||||
vec4 finalPos = vec4(0.0);
|
||||
vec4 finalNormal = vec4(0.0);
|
||||
vec4 finalTangent = vec4(0.0);
|
||||
for(int i = 0 ; i < 4; i++)
|
||||
{
|
||||
int boneIndex = int(vBoneIds[i]);
|
||||
if(boneIndex < 0) break;
|
||||
|
||||
gl_Position = uProjection * uView * vInstanceMatrix * bindPos;
|
||||
mat4 boneMatrix = uFinalBonesMatrix[boneIndex];
|
||||
float weight = vBoneWeights[i];
|
||||
|
||||
fPos = vec3(vInstanceMatrix * bindPos);
|
||||
fNormal = vec3(transpose(inverse(vInstanceMatrix)) * bindNormal);
|
||||
fTangent = vec3(transpose(inverse(vInstanceMatrix)) * bindTangent);
|
||||
finalPos += boneMatrix * bindPos * weight;
|
||||
finalNormal += boneMatrix * bindNormal * weight;
|
||||
finalTangent += boneMatrix * bindTangent * weight;
|
||||
}
|
||||
|
||||
gl_Position = uProjection * uView * vInstanceMatrix * finalPos;
|
||||
|
||||
fPos = vec3(vInstanceMatrix * finalPos);
|
||||
fNormal = vec3(transpose(inverse(vInstanceMatrix)) * finalNormal);
|
||||
fTangent = vec3(transpose(inverse(vInstanceMatrix)) * finalTangent);
|
||||
fTexCoords = vTexCoords;
|
||||
fTexLayer = vTexLayer;
|
||||
fColor = vColor;
|
||||
|
|
|
|||
|
|
@ -2,18 +2,38 @@
|
|||
|
||||
layout (location = 1) in vec3 vPos;
|
||||
layout (location = 2) in vec3 vNormal;
|
||||
layout (location = 7) in vec4 vBoneIds;
|
||||
layout (location = 8) in vec4 vBoneWeights;
|
||||
layout (location = 9) in mat4 vInstanceMatrix;
|
||||
layout (location = 13) in vec3 vMorphTarget;
|
||||
layout (location = 13) in vec3 vMorphTargetPos;
|
||||
|
||||
uniform mat4 uView;
|
||||
uniform vec3 uViewPos;
|
||||
uniform mat4 uProjection;
|
||||
uniform float uMorphTime;
|
||||
uniform mat4 uFinalBonesMatrix[250];
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 pos = vec3(vInstanceMatrix * vec4(mix(vPos, vMorphTarget, uMorphTime), 1.0));
|
||||
vec3 nor = mat3(transpose(inverse(vInstanceMatrix))) * vNormal;
|
||||
vec4 bindPos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0);
|
||||
vec4 bindNormal = vec4(vNormal, 1.0);
|
||||
|
||||
vec4 finalPos = vec4(0.0);
|
||||
vec4 finalNormal = vec4(0.0);
|
||||
for(int i = 0 ; i < 4; i++)
|
||||
{
|
||||
int boneIndex = int(vBoneIds[i]);
|
||||
if(boneIndex < 0) break;
|
||||
|
||||
mat4 boneMatrix = uFinalBonesMatrix[boneIndex];
|
||||
float weight = vBoneWeights[i];
|
||||
|
||||
finalPos += boneMatrix * bindPos * weight;
|
||||
finalNormal += boneMatrix * bindNormal * weight;
|
||||
}
|
||||
|
||||
vec3 pos = vec3(vInstanceMatrix * finalPos);
|
||||
vec3 nor = vec3(transpose(inverse(vInstanceMatrix)) * finalNormal);
|
||||
|
||||
float scaleFactor = distance(pos, uViewPos) * 0.0025;
|
||||
vec3 scaleVertex = pos + nor * scaleFactor;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using CUE4Parse_Conversion.Animations;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
|
||||
namespace FModel.Views.Snooper.Models.Animations;
|
||||
|
||||
|
|
@ -15,30 +17,37 @@ public class Animation : IDisposable
|
|||
|
||||
var sequence = anim.Sequences[0];
|
||||
MaxTime = sequence.NumFrames - 1;
|
||||
BoneTransforms = new Transform[skeleton.BonesTransformByIndex.Count][];
|
||||
for (var boneIndex = 0; boneIndex < BoneTransforms.Length; boneIndex++)
|
||||
BoneTransforms = new Transform[skeleton.UnrealSkeleton.ReferenceSkeleton.FinalRefBoneInfo.Length][];
|
||||
for (var trackIndex = 0; trackIndex < BoneTransforms.Length; trackIndex++)
|
||||
{
|
||||
var parentIndex = skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex].ParentIndex;
|
||||
var bone = skeleton.UnrealSkeleton.ReferenceSkeleton.FinalRefBoneInfo[trackIndex];
|
||||
if (!skeleton.BonesIndexByLoweredName.TryGetValue(bone.Name.Text.ToLower(), out var boneIndex))
|
||||
{
|
||||
BoneTransforms[trackIndex] = new Transform[sequence.NumFrames];
|
||||
continue;
|
||||
}
|
||||
if (!skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var originalTransform))
|
||||
throw new ArgumentNullException("no transform for bone " + boneIndex);
|
||||
throw new ArgumentNullException($"no transform for bone '{boneIndex}'");
|
||||
|
||||
var boneOrientation = originalTransform.Rotation;
|
||||
var bonePosition = originalTransform.Position;
|
||||
var boneScale = originalTransform.Scale;
|
||||
|
||||
BoneTransforms[boneIndex] = new Transform[sequence.NumFrames];
|
||||
for (var frame = 0; frame < BoneTransforms[boneIndex].Length; frame++)
|
||||
BoneTransforms[trackIndex] = new Transform[sequence.NumFrames];
|
||||
for (var frame = 0; frame < BoneTransforms[trackIndex].Length; frame++)
|
||||
{
|
||||
sequence.Tracks[boneIndex].GetBonePosition(frame, sequence.NumFrames, false, ref bonePosition, ref boneOrientation);
|
||||
if (CurrentTime < sequence.Tracks[boneIndex].KeyScale.Length)
|
||||
boneScale = sequence.Tracks[boneIndex].KeyScale[CurrentTime];
|
||||
sequence.Tracks[trackIndex].GetBonePosition(frame, sequence.NumFrames, false, ref bonePosition, ref boneOrientation);
|
||||
if (CurrentTime < sequence.Tracks[trackIndex].KeyScale.Length)
|
||||
boneScale = sequence.Tracks[trackIndex].KeyScale[CurrentTime];
|
||||
|
||||
// revert FixRotationKeys
|
||||
if (trackIndex > 0) boneOrientation.Conjugate();
|
||||
|
||||
boneOrientation.W *= -1;
|
||||
bonePosition *= Constants.SCALE_DOWN_RATIO;
|
||||
|
||||
BoneTransforms[boneIndex][frame] = new Transform
|
||||
BoneTransforms[trackIndex][frame] = new Transform
|
||||
{
|
||||
Relation = parentIndex >= 0 ? BoneTransforms[parentIndex][frame].Matrix : originalTransform.Relation,
|
||||
Relation = bone.ParentIndex >= 0 ? BoneTransforms[bone.ParentIndex][frame].Matrix : originalTransform.Relation,
|
||||
Rotation = boneOrientation,
|
||||
Position = bonePosition,
|
||||
Scale = boneScale
|
||||
|
|
|
|||
|
|
@ -12,16 +12,18 @@ public class Skeleton : IDisposable
|
|||
{
|
||||
public readonly USkeleton UnrealSkeleton;
|
||||
public readonly FReferenceSkeleton ReferenceSkeleton;
|
||||
public readonly Dictionary<string, int> BonesIndexByName;
|
||||
public readonly Dictionary<string, int> BonesIndexByLoweredName;
|
||||
public readonly Dictionary<int, Transform> BonesTransformByIndex;
|
||||
public readonly Dictionary<int, Matrix4x4> InvertedBonesMatrixByIndex;
|
||||
public readonly bool IsLoaded;
|
||||
|
||||
public Animation Anim;
|
||||
|
||||
public Skeleton()
|
||||
{
|
||||
BonesIndexByName = new Dictionary<string, int>();
|
||||
BonesIndexByLoweredName = new Dictionary<string, int>();
|
||||
BonesTransformByIndex = new Dictionary<int, Transform>();
|
||||
InvertedBonesMatrixByIndex = new Dictionary<int, Matrix4x4>();
|
||||
}
|
||||
|
||||
public Skeleton(FPackageIndex package, FReferenceSkeleton referenceSkeleton, Transform transform) : this()
|
||||
|
|
@ -30,15 +32,10 @@ public class Skeleton : IDisposable
|
|||
IsLoaded = UnrealSkeleton != null;
|
||||
if (!IsLoaded) return;
|
||||
|
||||
ReferenceSkeleton = UnrealSkeleton.ReferenceSkeleton;
|
||||
foreach ((var name, var boneIndex) in ReferenceSkeleton.FinalNameToIndexMap)
|
||||
{
|
||||
if (!referenceSkeleton.FinalNameToIndexMap.TryGetValue(name, out var newBoneIndex))
|
||||
continue;
|
||||
ReferenceSkeleton = referenceSkeleton;
|
||||
foreach ((var name, var boneIndex) in referenceSkeleton.FinalNameToIndexMap)
|
||||
BonesIndexByLoweredName[name.ToLower()] = boneIndex;
|
||||
|
||||
ReferenceSkeleton.FinalRefBonePose[boneIndex] = referenceSkeleton.FinalRefBonePose[newBoneIndex];
|
||||
}
|
||||
BonesIndexByName = ReferenceSkeleton.FinalNameToIndexMap;
|
||||
UpdateBoneMatrices(transform.Matrix);
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +47,7 @@ public class Skeleton : IDisposable
|
|||
public void UpdateBoneMatrices(Matrix4x4 matrix)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
foreach (var boneIndex in BonesIndexByName.Values)
|
||||
foreach (var boneIndex in BonesIndexByLoweredName.Values)
|
||||
{
|
||||
var bone = ReferenceSkeleton.FinalRefBonePose[boneIndex];
|
||||
var parentIndex = ReferenceSkeleton.FinalRefBoneInfo[boneIndex].ParentIndex;
|
||||
|
|
@ -69,22 +66,44 @@ public class Skeleton : IDisposable
|
|||
parentTransform = new Transform { Relation = matrix };
|
||||
|
||||
boneTransform.Relation = parentTransform.Matrix;
|
||||
Matrix4x4.Invert(boneTransform.Matrix, out var inverted);
|
||||
|
||||
BonesTransformByIndex[boneIndex] = boneTransform;
|
||||
InvertedBonesMatrixByIndex[boneIndex] = inverted;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPoseUniform(Shader shader)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
foreach ((var boneIndex, var transform) in BonesTransformByIndex)
|
||||
{
|
||||
if (boneIndex >= Constants.MAX_BONE_UNIFORM)
|
||||
break;
|
||||
shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", InvertedBonesMatrixByIndex[boneIndex] * transform.Matrix);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniform(Shader shader)
|
||||
{
|
||||
if (!IsLoaded || Anim == null) return;
|
||||
for (int boneIndex = 0; boneIndex < Anim.BoneTransforms.Length; boneIndex++)
|
||||
if (!IsLoaded) return;
|
||||
if (Anim == null) SetPoseUniform(shader);
|
||||
else foreach ((var boneName, var trackIndex) in UnrealSkeleton.ReferenceSkeleton.FinalNameToIndexMap)
|
||||
{
|
||||
shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", Anim.BoneTransforms[boneIndex][Anim.CurrentTime].Matrix);
|
||||
if (!BonesIndexByLoweredName.TryGetValue(boneName.ToLower(), out var boneIndex))
|
||||
continue;
|
||||
if (!InvertedBonesMatrixByIndex.TryGetValue(boneIndex, out var invertMatrix))
|
||||
throw new ArgumentNullException($"no inverse matrix for bone '{boneIndex}'");
|
||||
if (boneIndex >= Constants.MAX_BONE_UNIFORM)
|
||||
break;
|
||||
|
||||
shader.SetUniform($"uFinalBonesMatrix[{boneIndex}]", invertMatrix * Anim.BoneTransforms[trackIndex][Anim.CurrentTime].Matrix);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BonesIndexByName.Clear();
|
||||
BonesIndexByLoweredName.Clear();
|
||||
BonesTransformByIndex.Clear();
|
||||
Anim?.Dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
|
|
@ -21,6 +22,25 @@ using OpenTK.Graphics.OpenGL4;
|
|||
|
||||
namespace FModel.Views.Snooper.Models;
|
||||
|
||||
public class VertexAttribute
|
||||
{
|
||||
public int Size;
|
||||
public bool Enabled;
|
||||
}
|
||||
|
||||
public enum EAttribute
|
||||
{
|
||||
Index,
|
||||
Position,
|
||||
Normals,
|
||||
Tangent,
|
||||
UVs,
|
||||
Layer,
|
||||
Colors,
|
||||
BonesId,
|
||||
BonesWeight
|
||||
}
|
||||
|
||||
public class Model : IDisposable
|
||||
{
|
||||
private int _handle;
|
||||
|
|
@ -32,14 +52,26 @@ public class Model : IDisposable
|
|||
private BufferObject<Matrix4x4> _matrixVbo;
|
||||
private VertexArrayObject<float, uint> _vao;
|
||||
|
||||
private readonly UObject _export;
|
||||
protected readonly int VertexSize = 13; // VertexIndex + Position + Normal + Tangent + UV + TextureLayer
|
||||
protected int VertexSize => _vertexAttributes.Where(x => x.Enabled).Sum(x => x.Size);
|
||||
protected bool HasVertexColors => _vertexAttributes[(int) EAttribute.Colors].Enabled;
|
||||
private readonly List<VertexAttribute> _vertexAttributes = new()
|
||||
{
|
||||
new VertexAttribute { Size = 1, Enabled = true }, // VertexIndex
|
||||
new VertexAttribute { Size = 3, Enabled = true }, // Position
|
||||
new VertexAttribute { Size = 3, Enabled = true }, // Normal
|
||||
new VertexAttribute { Size = 3, Enabled = true }, // Tangent
|
||||
new VertexAttribute { Size = 2, Enabled = true }, // UV
|
||||
new VertexAttribute { Size = 1, Enabled = true }, // TextureLayer
|
||||
new VertexAttribute { Size = 4, Enabled = false }, // Colors
|
||||
new VertexAttribute { Size = 4, Enabled = false }, // BoneIds
|
||||
new VertexAttribute { Size = 4, Enabled = false } // BoneWeights
|
||||
};
|
||||
private const int _faceSize = 3;
|
||||
|
||||
private readonly UObject _export;
|
||||
public readonly string Path;
|
||||
public readonly string Name;
|
||||
public readonly string Type;
|
||||
public readonly bool HasVertexColors;
|
||||
public readonly int UvCount;
|
||||
public readonly FBox Box;
|
||||
public uint[] Indices;
|
||||
|
|
@ -117,7 +149,7 @@ public class Model : IDisposable
|
|||
{
|
||||
if (sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
|
||||
|
||||
if (!Skeleton.BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
|
||||
if (!Skeleton.BonesIndexByLoweredName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
|
||||
!Skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform))
|
||||
boneTransform = t;
|
||||
|
||||
|
|
@ -148,16 +180,9 @@ public class Model : IDisposable
|
|||
Materials[m] = new Material(unrealMaterial); else Materials[m] = new Material();
|
||||
}
|
||||
|
||||
if (lod.VertexColors is { Length: > 0})
|
||||
{
|
||||
HasVertexColors = true;
|
||||
VertexSize += 4; // + Color
|
||||
}
|
||||
|
||||
if (vertices is CSkelMeshVertex[])
|
||||
{
|
||||
VertexSize += 8; // + BoneIds + BoneWeights
|
||||
}
|
||||
_vertexAttributes[(int) EAttribute.Colors].Enabled = lod.VertexColors is { Length: > 0};
|
||||
_vertexAttributes[(int) EAttribute.BonesId].Enabled =
|
||||
_vertexAttributes[(int) EAttribute.BonesWeight].Enabled = vertices is CSkelMeshVertex[];
|
||||
|
||||
Indices = new uint[lod.Indices.Value.Length];
|
||||
for (int i = 0; i < Indices.Length; i++)
|
||||
|
|
@ -255,7 +280,7 @@ public class Model : IDisposable
|
|||
foreach (var socket in Sockets)
|
||||
{
|
||||
if (!HasSkeleton ||
|
||||
!Skeleton.BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
|
||||
!Skeleton.BonesIndexByLoweredName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
|
||||
!Skeleton.BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform))
|
||||
boneTransform = Transforms[SelectedInstance];
|
||||
|
||||
|
|
@ -307,15 +332,18 @@ public class Model : IDisposable
|
|||
_vbo = new BufferObject<float>(Vertices, BufferTarget.ArrayBuffer);
|
||||
_vao = new VertexArrayObject<float, uint>(_vbo, _ebo);
|
||||
|
||||
_vao.VertexAttributePointer(0, 1, VertexAttribPointerType.Int, VertexSize, 0); // vertex index
|
||||
_vao.VertexAttributePointer(1, 3, VertexAttribPointerType.Float, VertexSize, 1); // position
|
||||
_vao.VertexAttributePointer(2, 3, VertexAttribPointerType.Float, VertexSize, 4); // normal
|
||||
_vao.VertexAttributePointer(3, 3, VertexAttribPointerType.Float, VertexSize, 7); // tangent
|
||||
_vao.VertexAttributePointer(4, 2, VertexAttribPointerType.Float, VertexSize, 10); // uv
|
||||
if (!broken) _vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, VertexSize, 12); // texture index
|
||||
_vao.VertexAttributePointer(6, 4, VertexAttribPointerType.Float, VertexSize, 13); // color
|
||||
_vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Float, VertexSize, 17); // boneids
|
||||
_vao.VertexAttributePointer(8, 4, VertexAttribPointerType.Float, VertexSize, 21); // boneweights
|
||||
var offset = 0;
|
||||
for (int i = 0; i < _vertexAttributes.Count; i++)
|
||||
{
|
||||
var attribute = _vertexAttributes[i];
|
||||
if (!attribute.Enabled) continue;
|
||||
|
||||
if (i != 5 || !broken)
|
||||
{
|
||||
_vao.VertexAttributePointer((uint) i, attribute.Size, i == 0 ? VertexAttribPointerType.Int : VertexAttribPointerType.Float, VertexSize, offset);
|
||||
}
|
||||
offset += attribute.Size;
|
||||
}
|
||||
|
||||
SetupInstances(); // instanced models transform
|
||||
|
||||
|
|
@ -360,6 +388,7 @@ public class Model : IDisposable
|
|||
|
||||
_vao.Bind();
|
||||
shader.SetUniform("uMorphTime", MorphTime);
|
||||
if (HasSkeleton) Skeleton.SetUniform(shader);
|
||||
if (!outline)
|
||||
{
|
||||
shader.SetUniform("uUvCount", UvCount);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ public static class TextureHelper
|
|||
// only if it makes a big difference pls
|
||||
switch (_game)
|
||||
{
|
||||
// R: Whatever (AO / S / E / ...)
|
||||
// G: Roughness
|
||||
// B: Metallic
|
||||
case "hk_project":
|
||||
case "cosmicshake":
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
|
|
|
|||
|
|
@ -344,8 +344,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
|||
_saver.Value = model.TrySave(out _saver.Label, out _saver.Path);
|
||||
s.WindowShouldFreeze(false);
|
||||
}
|
||||
ImGui.BeginDisabled(true);
|
||||
// ImGui.BeginDisabled(!model.HasSkeleton);
|
||||
ImGui.BeginDisabled(!model.HasSkeleton);
|
||||
if (ImGui.Selectable("Animate"))
|
||||
{
|
||||
s.Renderer.Options.AnimateMesh(true);
|
||||
|
|
@ -449,7 +448,9 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
|||
{
|
||||
if (model.Skeleton.Anim != null)
|
||||
{
|
||||
ImGui.BeginDisabled(model.Skeleton.Anim.MaxTime == 0);
|
||||
ImGui.DragInt("Time", ref model.Skeleton.Anim.CurrentTime, 1, 0, model.Skeleton.Anim.MaxTime);
|
||||
ImGui.EndDisabled();
|
||||
}
|
||||
Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}");
|
||||
Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user