mirror of
https://github.com/4sval/FModel.git
synced 2026-08-07 11:36:03 -05:00
Some checks failed
FModel QA Builder / build (push) Has been cancelled
Co-authored-by: NotOfficer <marlon.john@hotmail.de> Co-authored-by: Krowe-moh <27891447+Krowe-moh@users.noreply.github.com> Co-authored-by: Chompster86 <chompster86@gmail.com> Co-authored-by: ScrubN <72096833+ScrubN@users.noreply.github.com> Co-authored-by: Masusder <59669685+Masusder@users.noreply.github.com> Co-authored-by: Tectfy <73559984+Tectfy@users.noreply.github.com> Co-authored-by: yarik0chka <64433879+yarik0chka@users.noreply.github.com>
204 lines
7.1 KiB
C#
204 lines
7.1 KiB
C#
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using CUE4Parse_Conversion.Dto;
|
|
using CUE4Parse.UE4.Assets.Exports.Animation;
|
|
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
|
using CUE4Parse.UE4.Objects.Core.Math;
|
|
using CUE4Parse.UE4.Objects.PhysicsEngine;
|
|
using CUE4Parse.UE4.Objects.UObject;
|
|
using FModel.Views.Snooper.Animations;
|
|
using FModel.Views.Snooper.Buffers;
|
|
using FModel.Views.Snooper.Shading;
|
|
using OpenTK.Graphics.OpenGL4;
|
|
|
|
namespace FModel.Views.Snooper.Models;
|
|
|
|
public class SkeletalModel : UModel<SkinnedMeshVertex>
|
|
{
|
|
private BufferObject<float> _morphVbo;
|
|
|
|
public readonly Skeleton Skeleton;
|
|
public readonly List<Morph> Morphs;
|
|
|
|
public bool HasMorphTargets => Morphs.Count > 0;
|
|
|
|
public float MorphTime;
|
|
|
|
public SkeletalModel(USkeletalMesh export, SkeletalMeshDto skeletalMesh, Transform transform = null)
|
|
: base(export, skeletalMesh.LODs[LodLevel], export.Materials, skeletalMesh.LODs[LodLevel].Vertices, skeletalMesh.LODs.Count, transform)
|
|
{
|
|
Box = skeletalMesh.Bounds * Constants.SCALE_DOWN_RATIO;
|
|
Skeleton = new Skeleton(export.ReferenceSkeleton);
|
|
|
|
var sockets = new List<FPackageIndex>();
|
|
sockets.AddRange(export.Sockets);
|
|
if (export.Skeleton.TryLoad(out USkeleton skeleton))
|
|
{
|
|
Skeleton.Name = skeleton.Name;
|
|
Skeleton.Guid = skeleton.Guid;
|
|
// Skeleton.Merge(skeleton.ReferenceSkeleton);
|
|
sockets.AddRange(skeleton.Sockets);
|
|
}
|
|
|
|
for (int i = 0; i < sockets.Count; i++)
|
|
{
|
|
if (sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
|
|
Sockets.Add(new Socket(socket));
|
|
}
|
|
|
|
if (export.PhysicsAsset.TryLoad(out UPhysicsAsset physicsAsset))
|
|
{
|
|
foreach (var skeletalBodySetup in physicsAsset.SkeletalBodySetups)
|
|
{
|
|
if (!skeletalBodySetup.TryLoad(out USkeletalBodySetup bodySetup) || bodySetup.AggGeom == null) continue;
|
|
foreach (var convexElem in bodySetup.AggGeom.ConvexElems)
|
|
{
|
|
Collisions.Add(new Collision(convexElem, bodySetup.BoneName));
|
|
}
|
|
foreach (var sphereElem in bodySetup.AggGeom.SphereElems)
|
|
{
|
|
Collisions.Add(new Collision(sphereElem, bodySetup.BoneName));
|
|
}
|
|
foreach (var boxElem in bodySetup.AggGeom.BoxElems)
|
|
{
|
|
Collisions.Add(new Collision(boxElem, bodySetup.BoneName));
|
|
}
|
|
foreach (var sphylElem in bodySetup.AggGeom.SphylElems)
|
|
{
|
|
Collisions.Add(new Collision(sphylElem, bodySetup.BoneName));
|
|
}
|
|
foreach (var taperedCapsuleElem in bodySetup.AggGeom.TaperedCapsuleElems)
|
|
{
|
|
Collisions.Add(new Collision(taperedCapsuleElem, bodySetup.BoneName));
|
|
}
|
|
}
|
|
}
|
|
|
|
Morphs = [];
|
|
if (export.MorphTargets.Length == 0) return;
|
|
|
|
export.PopulateMorphTargetVerticesData();
|
|
|
|
var verticesCount = Vertices.Length / VertexSize;
|
|
var cachedVertices = new float[verticesCount * Morph.VertexSize];
|
|
var vertexLookup = new Dictionary<uint, int>(verticesCount);
|
|
for (int i = 0; i < Vertices.Length; i += VertexSize)
|
|
{
|
|
var count = 0;
|
|
var baseIndex = i / VertexSize * Morph.VertexSize;
|
|
vertexLookup[(uint) Vertices[i]] = baseIndex;
|
|
{
|
|
cachedVertices[baseIndex + count++] = Vertices[i + 1];
|
|
cachedVertices[baseIndex + count++] = Vertices[i + 2];
|
|
cachedVertices[baseIndex + count++] = Vertices[i + 3];
|
|
cachedVertices[baseIndex + count++] = Vertices[i + 7];
|
|
cachedVertices[baseIndex + count++] = Vertices[i + 8];
|
|
cachedVertices[baseIndex + count++] = Vertices[i + 9];
|
|
}
|
|
}
|
|
|
|
foreach (var morph in export.MorphTargets)
|
|
{
|
|
if (!morph.TryLoad(out UMorphTarget morphTarget) || morphTarget.MorphLODModels.Length <= skeletalMesh.LODs[LodLevel].SourceLodIndex ||
|
|
morphTarget.MorphLODModels[skeletalMesh.LODs[LodLevel].SourceLodIndex].Vertices.Length < 1)
|
|
continue;
|
|
|
|
Morphs.Add(new Morph(cachedVertices, vertexLookup, morphTarget, skeletalMesh.LODs[LodLevel].SourceLodIndex));
|
|
}
|
|
}
|
|
|
|
public SkeletalModel(USkeleton export, FBox box) : base(export)
|
|
{
|
|
Indices = [];
|
|
Materials = [];
|
|
Vertices = [];
|
|
Sections = [];
|
|
AddInstance(Transform.Identity);
|
|
|
|
Box = box * Constants.SCALE_DOWN_RATIO;
|
|
Morphs = [];
|
|
Skeleton = new Skeleton(export.ReferenceSkeleton);
|
|
Skeleton.Name = export.Name;
|
|
Skeleton.Guid = export.Guid;
|
|
|
|
for (int i = 0; i < export.Sockets.Length; i++)
|
|
{
|
|
if (export.Sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
|
|
Sockets.Add(new Socket(socket));
|
|
}
|
|
|
|
IsVisible = true;
|
|
}
|
|
|
|
public override void Setup(Options options)
|
|
{
|
|
base.Setup(options);
|
|
|
|
Skeleton.Setup();
|
|
if (!HasMorphTargets) return;
|
|
|
|
for (int morph = 0; morph < Morphs.Count; morph++)
|
|
{
|
|
Morphs[morph].Setup();
|
|
if (morph == 0)
|
|
_morphVbo = new BufferObject<float>(Morphs[morph].Vertices, BufferTarget.ArrayBuffer);
|
|
}
|
|
|
|
Vao.Bind();
|
|
Vao.VertexAttributePointer(13, 3, VertexAttribPointerType.Float, Morph.VertexSize, 0); // morph position
|
|
Vao.VertexAttributePointer(14, 3, VertexAttribPointerType.Float, Morph.VertexSize, 0); // morph tangent
|
|
Vao.Unbind();
|
|
}
|
|
|
|
public override void RenderCollision(Shader shader)
|
|
{
|
|
base.RenderCollision(shader);
|
|
|
|
GL.Disable(EnableCap.DepthTest);
|
|
GL.Disable(EnableCap.CullFace);
|
|
GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Line);
|
|
foreach (var collision in Collisions)
|
|
{
|
|
var boneMatrix = Matrix4x4.Identity;
|
|
if (Skeleton.BonesByLoweredName.TryGetValue(collision.LoweredBoneName, out var bone))
|
|
boneMatrix = Skeleton.GetBoneMatrix(bone);
|
|
|
|
collision.Render(shader, boneMatrix);
|
|
}
|
|
GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Fill);
|
|
GL.Enable(EnableCap.CullFace);
|
|
GL.Enable(EnableCap.DepthTest);
|
|
}
|
|
|
|
public void Render(Shader shader)
|
|
{
|
|
shader.SetUniform("uMorphTime", MorphTime);
|
|
shader.SetUniform("uIsSpline", false);
|
|
Skeleton.Render(shader);
|
|
}
|
|
|
|
public void RenderBones(Shader shader)
|
|
{
|
|
shader.SetUniform("uInstanceMatrix", GetTransform().Matrix);
|
|
Skeleton.RenderBones();
|
|
}
|
|
|
|
public void UpdateMorph(int index)
|
|
{
|
|
_morphVbo.Update(Morphs[index].Vertices);
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
Skeleton?.Dispose();
|
|
if (HasMorphTargets) _morphVbo.Dispose();
|
|
foreach (var morph in Morphs)
|
|
{
|
|
morph?.Dispose();
|
|
}
|
|
Morphs.Clear();
|
|
|
|
base.Dispose();
|
|
}
|
|
}
|