mirror of
https://github.com/4sval/FModel.git
synced 2026-04-24 06:48:38 -05:00
fuck you bones position
that's a problem for future me
This commit is contained in:
parent
25de50818d
commit
ad6c4b9474
|
|
@ -1 +1 @@
|
|||
Subproject commit cc744cfb54e219d7f5d9440d30c4cd55b56afb3c
|
||||
Subproject commit 7a13a4e1c46e8a084f681e1a1a8702de695da3fa
|
||||
|
|
@ -194,6 +194,7 @@ outputColor = color * texture(in_fontTexture, texCoord);
|
|||
ImGui.Render();
|
||||
RenderImDrawData(ImGui.GetDrawData());
|
||||
}
|
||||
CheckGLError("End of frame");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -77,9 +77,15 @@ public partial class MainWindow
|
|||
_discordHandler.Initialize(_applicationView.CUE4Parse.Game);
|
||||
|
||||
#if DEBUG
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
_applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
"FortniteGame/Content/Athena/Artemis/Maps/Buildings/5x5/Artemis_5x5_Shop_AB.umap"));
|
||||
// await _threadWorkerView.Begin(cancellationToken =>
|
||||
// _applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
// "FortniteGame/Content/Characters/Player/Female/Medium/Bodies/F_MED_RoseDust/Meshes/F_MED_RoseDust.uasset"));
|
||||
// await _threadWorkerView.Begin(cancellationToken =>
|
||||
// _applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
// "fortnitegame/Content/Accessories/FORT_Backpacks/Backpack_M_MED_Despair/Meshes/M_MED_Despair_Pack.uasset"));
|
||||
// await _threadWorkerView.Begin(cancellationToken =>
|
||||
// _applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
// "FortniteGame/Content/Animation/Game/MainPlayer/Emotes/Acrobatic_Superhero/Emote_AcrobaticSuperhero_CMM.uasset"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,18 @@ layout (location = 3) in vec3 vTangent;
|
|||
layout (location = 4) in vec2 vTexCoords;
|
||||
layout (location = 5) in float vTexLayer;
|
||||
layout (location = 6) in vec4 vColor;
|
||||
layout (location = 7) in ivec4 vBoneIds;
|
||||
layout (location = 8) in vec4 vWeights;
|
||||
layout (location = 7) in vec4 vBoneIds;
|
||||
layout (location = 8) in vec4 vBoneWeights;
|
||||
layout (location = 9) in mat4 vInstanceMatrix;
|
||||
layout (location = 13) in vec3 vMorphTarget;
|
||||
|
||||
//const int MAX_BONES = 0;
|
||||
//const int MAX_BONE_INFLUENCE = 0;
|
||||
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uProjection;
|
||||
uniform float uMorphTime;
|
||||
//uniform mat4 uFinalBonesMatrix[MAX_BONES];
|
||||
|
||||
out vec3 fPos;
|
||||
out vec3 fNormal;
|
||||
|
|
@ -25,6 +29,18 @@ out vec4 fColor;
|
|||
void main()
|
||||
{
|
||||
vec4 pos = vec4(mix(vPos, vMorphTarget, uMorphTime), 1.0);
|
||||
// for(int i = 0 ; i < MAX_BONE_INFLUENCE; i++)
|
||||
// {
|
||||
// if(vBoneIds[i] == -1) continue;
|
||||
// if(vBoneIds[i] >= MAX_BONES)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// vec4 localPos = uFinalBonesMatrix[int(vBoneIds[i])] * pos;
|
||||
// pos += localPos * vBoneWeights[i];
|
||||
// }
|
||||
|
||||
gl_Position = uProjection * uView * vInstanceMatrix * pos;
|
||||
|
||||
fPos = vec3(vInstanceMatrix * pos);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,13 @@ public class CUE4ParseViewModel : ViewModel
|
|||
set => SetProperty(ref _modelIsOverwritingMaterial, value);
|
||||
}
|
||||
|
||||
private bool _modelIsWaitingAnimation;
|
||||
public bool ModelIsWaitingAnimation
|
||||
{
|
||||
get => _modelIsWaitingAnimation;
|
||||
set => SetProperty(ref _modelIsWaitingAnimation, value);
|
||||
}
|
||||
|
||||
private Snooper _snooper;
|
||||
public Snooper SnooperViewer
|
||||
{
|
||||
|
|
@ -790,6 +797,12 @@ public class CUE4ParseViewModel : ViewModel
|
|||
SnooperViewer.Run();
|
||||
return true;
|
||||
}
|
||||
case UAnimSequence a when ModelIsWaitingAnimation:
|
||||
{
|
||||
SnooperViewer.Renderer.Animate(a);
|
||||
SnooperViewer.Run();
|
||||
return true;
|
||||
}
|
||||
case UStaticMesh when UserSettings.Default.SaveStaticMeshes:
|
||||
case USkeletalMesh when UserSettings.Default.SaveSkeletalMeshes:
|
||||
case UMaterialInstance when UserSettings.Default.SaveMaterials:
|
||||
|
|
|
|||
64
FModel/Views/Snooper/Animation.cs
Normal file
64
FModel/Views/Snooper/Animation.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using CUE4Parse_Conversion.Animations;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
|
||||
namespace FModel.Views.Snooper;
|
||||
|
||||
public class Animation : IDisposable
|
||||
{
|
||||
public float CurrentTime;
|
||||
public float DeltaTime;
|
||||
public CAnimSet CurrentAnimation;
|
||||
public Matrix4x4[] FinalBonesMatrix;
|
||||
|
||||
public Animation(CAnimSet anim)
|
||||
{
|
||||
CurrentTime = 0f;
|
||||
CurrentAnimation = anim;
|
||||
|
||||
FinalBonesMatrix = new Matrix4x4[anim.TrackBoneNames.Length];
|
||||
for (int i = 0; i < FinalBonesMatrix.Length; i++)
|
||||
{
|
||||
FinalBonesMatrix[i] = Matrix4x4.Identity;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAnimation(float deltaTime)
|
||||
{
|
||||
DeltaTime = deltaTime;
|
||||
if (CurrentAnimation != null)
|
||||
{
|
||||
CurrentTime = deltaTime;
|
||||
CalculateBoneTransform();
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateBoneTransform()
|
||||
{
|
||||
var sequence = CurrentAnimation.Sequences[0];
|
||||
for (int boneIndex = 0; boneIndex < FinalBonesMatrix.Length; boneIndex++)
|
||||
{
|
||||
var boneOrientation = FQuat.Identity;
|
||||
var bonePosition = FVector.ZeroVector;
|
||||
sequence.Tracks[boneIndex].GetBonePosition(CurrentTime, sequence.NumFrames, false, ref bonePosition, ref boneOrientation);
|
||||
|
||||
boneOrientation *= CurrentAnimation.BonePositions[boneIndex].Orientation;
|
||||
bonePosition = boneOrientation.RotateVector(bonePosition);
|
||||
bonePosition *= Constants.SCALE_DOWN_RATIO;
|
||||
if (CurrentAnimation.TrackBoneNames[boneIndex].Text == "pelvis")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FinalBonesMatrix[boneIndex] =
|
||||
Matrix4x4.CreateFromQuaternion(boneOrientation) *
|
||||
Matrix4x4.CreateTranslation(bonePosition.ToMapVector());
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -28,14 +28,15 @@ public class Model : IDisposable
|
|||
public readonly string Name;
|
||||
public readonly string Type;
|
||||
public readonly bool HasVertexColors;
|
||||
public readonly bool HasBones;
|
||||
public readonly bool HasMorphTargets;
|
||||
public readonly int NumTexCoords;
|
||||
public uint[] Indices;
|
||||
public float[] Vertices;
|
||||
public Section[] Sections;
|
||||
public Material[] Materials;
|
||||
public readonly List<CSkelMeshBone> Skeleton;
|
||||
|
||||
public bool HasSkeleton => Skeleton is { IsLoaded: true };
|
||||
public readonly Skeleton Skeleton;
|
||||
|
||||
public int TransformsCount;
|
||||
public readonly List<Transform> Transforms;
|
||||
|
|
@ -50,7 +51,6 @@ public class Model : IDisposable
|
|||
public bool bVertexNormals;
|
||||
public bool bVertexTangent;
|
||||
public bool bVertexTexCoords;
|
||||
public bool DisplayBones;
|
||||
public int SelectedInstance;
|
||||
public float MorphTime;
|
||||
|
||||
|
|
@ -63,10 +63,10 @@ public class Model : IDisposable
|
|||
}
|
||||
|
||||
public Model(string name, string type, ResolvedObject[] materials, CStaticMesh staticMesh) : this(name, type, materials, staticMesh, Transform.Identity) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, CStaticMesh staticMesh, Transform transform) : this(name, type, materials, staticMesh.LODs[0], staticMesh.LODs[0].Verts, transform) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, CSkeletalMesh skeletalMesh) : this(name, type, materials, skeletalMesh, Transform.Identity) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, CSkeletalMesh skeletalMesh, Transform transform) : this(name, type, materials, skeletalMesh.LODs[0], skeletalMesh.LODs[0].Verts, transform, skeletalMesh.RefSkeleton) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, FPackageIndex[] morphTargets, CSkeletalMesh skeletalMesh) : this(name, type, materials, skeletalMesh)
|
||||
public Model(string name, string type, ResolvedObject[] materials, CStaticMesh staticMesh, Transform transform) : this(name, type, materials, null, staticMesh.LODs[0], staticMesh.LODs[0].Verts, transform) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, FPackageIndex skeleton, CSkeletalMesh skeletalMesh) : this(name, type, materials, skeleton, skeletalMesh, Transform.Identity) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, FPackageIndex skeleton, CSkeletalMesh skeletalMesh, Transform transform) : this(name, type, materials, skeleton, skeletalMesh.LODs[0], skeletalMesh.LODs[0].Verts, transform) {}
|
||||
public Model(string name, string type, ResolvedObject[] materials, FPackageIndex skeleton, FPackageIndex[] morphTargets, CSkeletalMesh skeletalMesh) : this(name, type, materials, skeleton, skeletalMesh)
|
||||
{
|
||||
if (morphTargets is not { Length: > 0 })
|
||||
return;
|
||||
|
|
@ -79,7 +79,7 @@ public class Model : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private Model(string name, string type, ResolvedObject[] materials, CBaseMeshLod lod, CMeshVertex[] vertices, Transform transform, List<CSkelMeshBone> skeleton = null) : this(name, type)
|
||||
private Model(string name, string type, ResolvedObject[] materials, FPackageIndex skeleton, CBaseMeshLod lod, CMeshVertex[] vertices, Transform transform = null) : this(name, type)
|
||||
{
|
||||
NumTexCoords = lod.NumTexCoords;
|
||||
|
||||
|
|
@ -96,10 +96,9 @@ public class Model : IDisposable
|
|||
_vertexSize += 4; // + Color
|
||||
}
|
||||
|
||||
if (skeleton is { Count: > 0 })
|
||||
if (skeleton != null)
|
||||
{
|
||||
HasBones = true;
|
||||
Skeleton = skeleton;
|
||||
Skeleton = new Skeleton(skeleton);
|
||||
_vertexSize += 8; // + BoneIds + BoneWeights
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +145,7 @@ public class Model : IDisposable
|
|||
Vertices[baseIndex + count++] = color.A;
|
||||
}
|
||||
|
||||
if (HasBones)
|
||||
if (HasSkeleton)
|
||||
{
|
||||
var skelVert = (CSkelMeshVertex) vert;
|
||||
var weightsHash = skelVert.UnpackWeights();
|
||||
|
|
@ -165,7 +164,7 @@ public class Model : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
AddInstance(transform);
|
||||
AddInstance(transform ?? Transform.Identity);
|
||||
}
|
||||
|
||||
public void AddInstance(Transform transform)
|
||||
|
|
@ -212,7 +211,7 @@ public class Model : IDisposable
|
|||
_vao.VertexAttributePointer(4, 2, VertexAttribPointerType.Float, _vertexSize, 10); // uv
|
||||
_vao.VertexAttributePointer(5, 1, VertexAttribPointerType.Float, _vertexSize, 12); // texture index
|
||||
_vao.VertexAttributePointer(6, 4, VertexAttribPointerType.Float, _vertexSize, 13); // color
|
||||
_vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Int, _vertexSize, 17); // boneids
|
||||
_vao.VertexAttributePointer(7, 4, VertexAttribPointerType.Float, _vertexSize, 17); // boneids
|
||||
_vao.VertexAttributePointer(8, 4, VertexAttribPointerType.Float, _vertexSize, 21); // boneweights
|
||||
|
||||
SetupInstances(); // instanced models transform
|
||||
|
|
|
|||
|
|
@ -50,4 +50,9 @@ public class Options
|
|||
{
|
||||
Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsOverwritingMaterial = value;
|
||||
}
|
||||
|
||||
public void AnimateMesh(bool value)
|
||||
{
|
||||
Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation = value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using CUE4Parse_Conversion.Animations;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
|
|
@ -55,6 +57,16 @@ public class Renderer : IDisposable
|
|||
Settings.SwapMaterial(false);
|
||||
}
|
||||
|
||||
public void Animate(UAnimSequence animSequence)
|
||||
{
|
||||
if (!Cache.Models.TryGetValue(Settings.SelectedModel, out var model) || !model.Skeleton.IsLoaded ||
|
||||
model.Skeleton?.RefSkel.ConvertAnims(animSequence) is not { } anim || anim.Sequences.Count == 0)
|
||||
return;
|
||||
|
||||
model.Skeleton.Anim = new Animation(anim);
|
||||
Settings.AnimateMesh(false);
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
_shader = new Shader();
|
||||
|
|
@ -122,7 +134,7 @@ public class Renderer : IDisposable
|
|||
var guid = Guid.NewGuid();
|
||||
if (Cache.Models.ContainsKey(guid) || !original.TryConvert(out var mesh)) return null;
|
||||
|
||||
Cache.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, original.MorphTargets, mesh);
|
||||
Cache.Models[guid] = new Model(original.Name, original.ExportType, original.Materials, original.Skeleton, original.MorphTargets, mesh);
|
||||
Settings.SelectModel(guid);
|
||||
return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,12 +78,14 @@ public class Shader : IDisposable
|
|||
GL.Uniform1(location, value);
|
||||
}
|
||||
|
||||
public unsafe void SetUniform(string name, Matrix4 value)
|
||||
public unsafe void SetUniform(string name, Matrix4 value) => UniformMatrix4(name, (float*) &value);
|
||||
public unsafe void SetUniform(string name, System.Numerics.Matrix4x4 value) => UniformMatrix4(name, (float*) &value);
|
||||
public unsafe void UniformMatrix4(string name, float* value)
|
||||
{
|
||||
//A new overload has been created for setting a uniform so we can use the transform in our shader.
|
||||
int location = GL.GetUniformLocation(_handle, name);
|
||||
ThrowIfNotFound(location, name);
|
||||
GL.UniformMatrix4(location, 1, false, (float*) &value);
|
||||
GL.UniformMatrix4(location, 1, false, value);
|
||||
}
|
||||
|
||||
public void SetUniform(string name, bool value) => SetUniform(name, Convert.ToUInt32(value));
|
||||
|
|
@ -124,7 +126,7 @@ public class Shader : IDisposable
|
|||
{
|
||||
if (location == -1)
|
||||
{
|
||||
// throw new Exception($"{name} uniform not found on shader.");
|
||||
throw new Exception($"{name} uniform not found on shader.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
52
FModel/Views/Snooper/Skeleton.cs
Normal file
52
FModel/Views/Snooper/Skeleton.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
|
||||
namespace FModel.Views.Snooper;
|
||||
|
||||
public class Skeleton : IDisposable
|
||||
{
|
||||
public readonly USkeleton RefSkel;
|
||||
public readonly bool IsLoaded;
|
||||
public readonly Socket[] Sockets;
|
||||
|
||||
public Animation Anim;
|
||||
|
||||
public Skeleton(FPackageIndex package)
|
||||
{
|
||||
RefSkel = package.Load<USkeleton>();
|
||||
if (RefSkel == null) return;
|
||||
|
||||
IsLoaded = true;
|
||||
Sockets = new Socket[RefSkel.Sockets.Length];
|
||||
for (int i = 0; i < Sockets.Length; i++)
|
||||
{
|
||||
if (RefSkel.Sockets[i].Load<USkeletalMeshSocket>() is not { } socket ||
|
||||
!RefSkel.ReferenceSkeleton.FinalNameToIndexMap.TryGetValue(socket.BoneName.Text, out var boneIndex))
|
||||
continue;
|
||||
|
||||
var t = RefSkel.ReferenceSkeleton.FinalRefBonePose[boneIndex];
|
||||
var transform = Transform.Identity;
|
||||
transform.Position = t.Translation.ToMapVector() * Constants.SCALE_DOWN_RATIO;
|
||||
transform.Rotation = t.Rotator();
|
||||
transform.Scale = t.Scale3D.ToMapVector();
|
||||
|
||||
Sockets[i] = new Socket(socket, transform);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniform(Shader shader)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
for (var i = 0; i < Anim?.FinalBonesMatrix.Length; i++)
|
||||
{
|
||||
shader.SetUniform($"uFinalBonesMatrix[{i}]", Anim.FinalBonesMatrix[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -37,91 +37,27 @@ public class SnimGui
|
|||
DrawDockSpace(s.Size);
|
||||
DrawNavbar();
|
||||
|
||||
ImGui.Begin("Camera");
|
||||
ImGui.DragFloat("Speed", ref s.Camera.Speed, 0.01f, 0.05f);
|
||||
ImGui.DragFloat("Far Plane", ref s.Camera.Far, 0.1f, 5f, s.Camera.Far * 2f, "%.2f m", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.End();
|
||||
ImGui.Begin("World");
|
||||
ImGui.Checkbox("Diffuse Only", ref s.Renderer.bDiffuseOnly);
|
||||
ImGui.End();
|
||||
ImGui.Begin("Timeline");
|
||||
ImGui.End();
|
||||
if (ImGui.Begin("Materials"))
|
||||
{
|
||||
PushStyleCompact();
|
||||
var guid = s.Renderer.Settings.SelectedModel;
|
||||
if (s.Renderer.Cache.Models.TryGetValue(guid, out var model) &&
|
||||
s.Renderer.Settings.TryGetSection(model, out var section))
|
||||
{
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
foreach ((string key, float value) in material.Parameters.Scalars)
|
||||
{
|
||||
ImGui.Text($"{key}: {value}");
|
||||
}
|
||||
ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing();
|
||||
foreach ((string key, FLinearColor value) in material.Parameters.Colors)
|
||||
{
|
||||
ImGui.Text(key); ImGui.SameLine();
|
||||
ImGui.ColorButton(key, new Vector4(value.R, value.G, value.B, value.A));
|
||||
}
|
||||
}
|
||||
else NoMeshSelected();
|
||||
PopStyleCompact();
|
||||
ImGui.End();
|
||||
}
|
||||
if (ImGui.Begin("Textures"))
|
||||
{
|
||||
PushStyleCompact();
|
||||
var guid = s.Renderer.Settings.SelectedModel;
|
||||
if (s.Renderer.Cache.Models.TryGetValue(guid, out var model) &&
|
||||
s.Renderer.Settings.TryGetSection(model, out var section))
|
||||
{
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
foreach ((string key, UUnrealMaterial value) in material.Parameters.Textures)
|
||||
{
|
||||
ImGui.Text($"{key}: {value.Name}");
|
||||
}
|
||||
}
|
||||
else NoMeshSelected();
|
||||
PopStyleCompact();
|
||||
ImGui.End();
|
||||
}
|
||||
if (ImGui.Begin("Parameters"))
|
||||
{
|
||||
PushStyleCompact();
|
||||
var guid = s.Renderer.Settings.SelectedModel;
|
||||
if (s.Renderer.Cache.Models.TryGetValue(guid, out var model) &&
|
||||
s.Renderer.Settings.TryGetSection(model, out var section))
|
||||
{
|
||||
const int width = 50;
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
ImGui.Checkbox("Show Section", ref section.Show);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("Emissive Multiplier", ref material.EmissiveMult, .01f, 0f);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("UV Scale", ref material.UVScale, .01f, 0f);
|
||||
if (material.HasM)
|
||||
{
|
||||
ImGui.ColorEdit3("Skin Boost Color", ref material.M.SkinBoost.Color, ImGuiColorEditFlags.NoInputs);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("Skin Boost Exponent", ref material.M.SkinBoost.Exponent, .01f, 0f);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("AmbientOcclusion", ref material.M.AmbientOcclusion, .01f, 0f, 1f);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("Cavity", ref material.M.Cavity, .01f, 0f, 1f);
|
||||
}
|
||||
}
|
||||
else NoMeshSelected();
|
||||
PopStyleCompact();
|
||||
ImGui.End();
|
||||
}
|
||||
SectionWindow("Materials", s.Renderer, DrawMaterials);
|
||||
SectionWindow("Textures", s.Renderer, DrawTextures);
|
||||
SectionWindow("Parameters", s.Renderer, DrawParameters);
|
||||
SectionWindow("UV Channels", s.Renderer, DrawUvChannels);
|
||||
|
||||
Window("Timeline", () => {});
|
||||
Window("Camera", () =>
|
||||
{
|
||||
ImGui.DragFloat("Speed", ref s.Camera.Speed, 0.01f, 0.05f);
|
||||
ImGui.DragFloat("Far Plane", ref s.Camera.Far, 0.1f, 5f, s.Camera.Far * 2f, "%.2f m", ImGuiSliderFlags.AlwaysClamp);
|
||||
});
|
||||
Window("World", () => ImGui.Checkbox("Diffuse Only", ref s.Renderer.bDiffuseOnly));
|
||||
Window("Outliner", () => DrawOuliner(s));
|
||||
Window("Sockets", () => DrawSockets(s));
|
||||
|
||||
MeshWindow("Transform", s.Renderer, model => DrawTransform(model, s.Camera.Speed / 100f));
|
||||
MeshWindow("Details", s.Renderer, model => DrawDetails(model, s));
|
||||
|
||||
DrawUvChannels(s);
|
||||
DrawTransform(s);
|
||||
DrawDetails(s);
|
||||
DrawOuliner(s);
|
||||
Draw3DViewport(s);
|
||||
// last render will always be on top
|
||||
// order by decreasing importance
|
||||
|
||||
Controller.Render();
|
||||
|
||||
ImGuiController.CheckGLError("End of frame");
|
||||
}
|
||||
|
||||
private void DrawDockSpace(Vector2i size)
|
||||
|
|
@ -170,386 +106,438 @@ public class SnimGui
|
|||
|
||||
private void DrawOuliner(Snooper s)
|
||||
{
|
||||
if (ImGui.Begin("Outliner"))
|
||||
if (ImGui.BeginTable("Items", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable))
|
||||
{
|
||||
PushStyleCompact();
|
||||
ImGui.TableSetupColumn("Instance", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Channels", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Name");
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
if (ImGui.BeginTable("Items", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable))
|
||||
var i = 0;
|
||||
foreach ((FGuid guid, Model model) in s.Renderer.Cache.Models)
|
||||
{
|
||||
ImGui.TableSetupColumn("Instance", ImGuiTableColumnFlags.NoHeaderWidth | ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Channels", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Name");
|
||||
ImGui.PushID(i);
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
if (!model.Show)
|
||||
{
|
||||
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f)));
|
||||
}
|
||||
|
||||
ImGui.Text(model.TransformsCount.ToString("D"));
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(model.NumTexCoords.ToString("D"));
|
||||
ImGui.TableNextColumn();
|
||||
model.IsSelected = s.Renderer.Settings.SelectedModel == guid;
|
||||
if (ImGui.Selectable(model.Name, model.IsSelected, ImGuiSelectableFlags.SpanAllColumns))
|
||||
{
|
||||
s.Renderer.Settings.SelectModel(guid);
|
||||
}
|
||||
if (ImGui.BeginPopupContextItem())
|
||||
{
|
||||
s.Renderer.Settings.SelectModel(guid);
|
||||
ImGui.BeginDisabled(!model.HasSkeleton);
|
||||
if (ImGui.Selectable("Animate"))
|
||||
{
|
||||
s.Renderer.Settings.AnimateMesh(true);
|
||||
s.WindowShouldClose(true, false);
|
||||
}
|
||||
ImGui.Separator();
|
||||
ImGui.EndDisabled();
|
||||
if (ImGui.Selectable("Delete")) s.Renderer.Cache.Models.Remove(guid);
|
||||
if (ImGui.Selectable("Deselect")) s.Renderer.Settings.SelectModel(Guid.Empty);
|
||||
if (ImGui.Selectable("Copy Name to Clipboard")) ImGui.SetClipboardText(model.Name);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
ImGui.PopID();
|
||||
i++;
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSockets(Snooper s)
|
||||
{
|
||||
foreach (var model in s.Renderer.Cache.Models.Values)
|
||||
{
|
||||
if (!model.HasSkeleton) return;
|
||||
if (ImGui.TreeNode($"{model.Name} [{model.Skeleton.Sockets.Length}]"))
|
||||
{
|
||||
foreach (var socket in model.Skeleton.Sockets)
|
||||
{
|
||||
ImGui.Text($"{socket.Name} attached to {socket.Bone}");
|
||||
ImGui.Text($"P: {socket.Transform.Position}");
|
||||
ImGui.Text($"R: {socket.Transform.Rotation}");
|
||||
ImGui.Text($"S: {socket.Transform.Scale}");
|
||||
if (ImGui.Button("Attach"))
|
||||
{
|
||||
var guid = s.Renderer.Settings.SelectedModel;
|
||||
if (s.Renderer.Cache.Models.TryGetValue(guid, out var selected))
|
||||
{
|
||||
selected.Transforms[selected.SelectedInstance] = socket.Transform;
|
||||
selected.UpdateMatrix(selected.SelectedInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDetails(Model model, Snooper s)
|
||||
{
|
||||
ImGui.Text($"Entity: ({model.Type}) {model.Name}");
|
||||
ImGui.Text($"Guid: {s.Renderer.Settings.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}");
|
||||
// if (model.Skeleton.Anim != null)
|
||||
// {
|
||||
// ImGui.DragFloat("Time", ref model.Skeleton.Anim.CurrentTime, 1.0f, 0.0f, 115.0f, "%.1f s");
|
||||
// model.Skeleton.Anim.CalculateBoneTransform();
|
||||
// }
|
||||
|
||||
ImGui.Columns(3, "Actions", false);
|
||||
if (ImGui.Button("Go To"))
|
||||
{
|
||||
var instancePos = model.Transforms[model.SelectedInstance].Position;
|
||||
s.Camera.Position = new Vector3(instancePos.X, instancePos.Y, instancePos.Z);
|
||||
}
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Show", ref model.Show);
|
||||
ImGui.NextColumn(); ImGui.BeginDisabled(!model.Show); ImGui.Checkbox("Wire", ref model.Wireframe); ImGui.EndDisabled();
|
||||
ImGui.Columns(4);
|
||||
ImGui.NextColumn(); ImGui.BeginDisabled(!model.HasVertexColors); ImGui.Checkbox("Colors", ref model.bVertexColors); ImGui.EndDisabled();
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Normals", ref model.bVertexNormals);
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Tangent", ref model.bVertexTangent);
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Coords", ref model.bVertexTexCoords);
|
||||
ImGui.Columns(1);
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (ImGui.BeginTabBar("tabbar_details", ImGuiTabBarFlags.None))
|
||||
{
|
||||
if (ImGui.BeginTabItem("Sections") && ImGui.BeginTable("table_sections", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable))
|
||||
{
|
||||
ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Material");
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
var i = 0;
|
||||
foreach ((FGuid guid, Model model) in s.Renderer.Cache.Models)
|
||||
var swap = false;
|
||||
for (var i = 0; i < model.Sections.Length; i++)
|
||||
{
|
||||
var section = model.Sections[i];
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
|
||||
ImGui.PushID(i);
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
if (!model.Show)
|
||||
if (!section.Show)
|
||||
{
|
||||
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f)));
|
||||
}
|
||||
|
||||
ImGui.Text(model.TransformsCount.ToString("D"));
|
||||
ImGui.Text(section.MaterialIndex.ToString("D"));
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(model.NumTexCoords.ToString("D"));
|
||||
ImGui.TableNextColumn();
|
||||
model.IsSelected = s.Renderer.Settings.SelectedModel == guid;
|
||||
if (ImGui.Selectable(model.Name, model.IsSelected, ImGuiSelectableFlags.SpanAllColumns))
|
||||
if (ImGui.Selectable(material.Name, s.Renderer.Settings.SelectedSection == i, ImGuiSelectableFlags.SpanAllColumns))
|
||||
{
|
||||
s.Renderer.Settings.SelectModel(guid);
|
||||
s.Renderer.Settings.SelectSection(i);
|
||||
}
|
||||
if (ImGui.BeginPopupContextItem())
|
||||
{
|
||||
s.Renderer.Settings.SelectModel(guid);
|
||||
if (ImGui.Selectable("Delete")) s.Renderer.Cache.Models.Remove(guid);
|
||||
if (ImGui.Selectable("Deselect")) s.Renderer.Settings.SelectModel(Guid.Empty);
|
||||
if (ImGui.Selectable("Copy Name to Clipboard")) ImGui.SetClipboardText(model.Name);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
ImGui.PopID();
|
||||
i++;
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
PopStyleCompact();
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDetails(Snooper s)
|
||||
{
|
||||
if (ImGui.Begin("Details"))
|
||||
{
|
||||
PushStyleCompact();
|
||||
var guid = s.Renderer.Settings.SelectedModel;
|
||||
if (s.Renderer.Cache.Models.TryGetValue(guid, out var model))
|
||||
{
|
||||
ImGui.Text($"Entity: ({model.Type}) {model.Name}");
|
||||
ImGui.Text($"Guid: {guid.ToString(EGuidFormats.UniqueObjectGuid)}");
|
||||
|
||||
ImGui.Columns(3, "Actions", false);
|
||||
if (ImGui.Button("Go To"))
|
||||
{
|
||||
var instancePos = model.Transforms[model.SelectedInstance].Position;
|
||||
s.Camera.Position = new Vector3(instancePos.X, instancePos.Y, instancePos.Z);
|
||||
}
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Show", ref model.Show);
|
||||
ImGui.NextColumn(); ImGui.BeginDisabled(!model.Show); ImGui.Checkbox("Wire", ref model.Wireframe); ImGui.EndDisabled();
|
||||
ImGui.Columns(4);
|
||||
ImGui.NextColumn(); ImGui.BeginDisabled(!model.HasVertexColors); ImGui.Checkbox("Colors", ref model.bVertexColors); ImGui.EndDisabled();
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Normals", ref model.bVertexNormals);
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Tangent", ref model.bVertexTangent);
|
||||
ImGui.NextColumn(); ImGui.Checkbox("Coords", ref model.bVertexTexCoords);
|
||||
ImGui.Columns(1);
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (ImGui.BeginTabBar("tabbar_details", ImGuiTabBarFlags.None))
|
||||
{
|
||||
if (ImGui.BeginTabItem("Sections") && ImGui.BeginTable("table_sections", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable))
|
||||
{
|
||||
ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Material");
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
var swap = false;
|
||||
for (var i = 0; i < model.Sections.Length; i++)
|
||||
s.Renderer.Settings.SelectSection(i);
|
||||
if (ImGui.Selectable("Swap"))
|
||||
{
|
||||
var section = model.Sections[i];
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
|
||||
ImGui.PushID(i);
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
if (!section.Show)
|
||||
if (_swapAwareness)
|
||||
{
|
||||
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(new Vector4(1, 0, 0, .5f)));
|
||||
}
|
||||
|
||||
ImGui.Text(section.MaterialIndex.ToString("D"));
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Selectable(material.Name, s.Renderer.Settings.SelectedSection == i, ImGuiSelectableFlags.SpanAllColumns))
|
||||
{
|
||||
s.Renderer.Settings.SelectSection(i);
|
||||
}
|
||||
if (ImGui.BeginPopupContextItem())
|
||||
{
|
||||
s.Renderer.Settings.SelectSection(i);
|
||||
if (ImGui.Selectable("Swap"))
|
||||
{
|
||||
if (_swapAwareness)
|
||||
{
|
||||
s.Renderer.Settings.SwapMaterial(true);
|
||||
s.WindowShouldClose(true, false);
|
||||
}
|
||||
else swap = true;
|
||||
}
|
||||
if (ImGui.Selectable("Copy Name to Clipboard")) ImGui.SetClipboardText(material.Name);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
ImGui.PopID();
|
||||
}
|
||||
ImGui.EndTable();
|
||||
|
||||
var p_open = true;
|
||||
if (swap) ImGui.OpenPopup("Swap?");
|
||||
ImGui.SetNextWindowPos(ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, new Vector2(.5f));
|
||||
if (ImGui.BeginPopupModal("Swap?", ref p_open, ImGuiWindowFlags.AlwaysAutoResize))
|
||||
{
|
||||
ImGui.TextWrapped("You're about to swap a material.\nThe window will close for you to extract a material!\n\n");
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
||||
ImGui.Checkbox("Got it! Don't show me again", ref _swapAwareness);
|
||||
ImGui.PopStyleVar();
|
||||
|
||||
var size = new Vector2(120, 0);
|
||||
if (ImGui.Button("OK", size))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
s.Renderer.Settings.SwapMaterial(true);
|
||||
s.WindowShouldClose(true, false);
|
||||
}
|
||||
|
||||
ImGui.SetItemDefaultFocus();
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Cancel", size))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui.EndPopup();
|
||||
else swap = true;
|
||||
}
|
||||
if (ImGui.Selectable("Copy Name to Clipboard")) ImGui.SetClipboardText(material.Name);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
ImGui.PopID();
|
||||
}
|
||||
ImGui.EndTable();
|
||||
|
||||
ImGui.EndTabItem();
|
||||
var p_open = true;
|
||||
if (swap) ImGui.OpenPopup("Swap?");
|
||||
ImGui.SetNextWindowPos(ImGui.GetMainViewport().GetCenter(), ImGuiCond.Appearing, new Vector2(.5f));
|
||||
if (ImGui.BeginPopupModal("Swap?", ref p_open, ImGuiWindowFlags.AlwaysAutoResize))
|
||||
{
|
||||
ImGui.TextWrapped("You're about to swap a material.\nThe window will close for you to extract a material!\n\n");
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero);
|
||||
ImGui.Checkbox("Got it! Don't show me again", ref _swapAwareness);
|
||||
ImGui.PopStyleVar();
|
||||
|
||||
var size = new Vector2(120, 0);
|
||||
if (ImGui.Button("OK", size))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
s.Renderer.Settings.SwapMaterial(true);
|
||||
s.WindowShouldClose(true, false);
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Morph Targets"))
|
||||
ImGui.SetItemDefaultFocus();
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Cancel", size))
|
||||
{
|
||||
if (model.HasMorphTargets)
|
||||
{
|
||||
const float width = 10;
|
||||
var region = ImGui.GetContentRegionAvail();
|
||||
var box = new Vector2(region.X - width, region.Y / 1.5f);
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (ImGui.BeginListBox("", box))
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Morph Targets"))
|
||||
{
|
||||
if (model.HasMorphTargets)
|
||||
{
|
||||
const float width = 10;
|
||||
var region = ImGui.GetContentRegionAvail();
|
||||
var box = new Vector2(region.X - width, region.Y / 1.5f);
|
||||
|
||||
if (ImGui.BeginListBox("", box))
|
||||
{
|
||||
for (int i = 0; i < model.Morphs.Length; i++)
|
||||
{
|
||||
ImGui.PushID(i);
|
||||
if (ImGui.Selectable(model.Morphs[i].Name, s.Renderer.Settings.SelectedMorph == i))
|
||||
{
|
||||
for (int i = 0; i < model.Morphs.Length; i++)
|
||||
{
|
||||
ImGui.PushID(i);
|
||||
if (ImGui.Selectable(model.Morphs[i].Name, s.Renderer.Settings.SelectedMorph == i))
|
||||
{
|
||||
s.Renderer.Settings.SelectMorph(i, model);
|
||||
}
|
||||
ImGui.PopID();
|
||||
}
|
||||
ImGui.EndListBox();
|
||||
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(2f, 0f));
|
||||
ImGui.SameLine(); ImGui.PushID(99);
|
||||
ImGui.VSliderFloat("", box with { X = width }, ref model.MorphTime, 0.0f, 1.0f, "", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.PopID(); ImGui.PopStyleVar();
|
||||
ImGui.Spacing();
|
||||
ImGui.Text($"Time: {model.MorphTime:P}%");
|
||||
s.Renderer.Settings.SelectMorph(i, model);
|
||||
}
|
||||
ImGui.PopID();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(_errorColor, "mesh has no morph targets");
|
||||
}
|
||||
ImGui.EndListBox();
|
||||
|
||||
ImGui.EndTabItem();
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(2f, 0f));
|
||||
ImGui.SameLine(); ImGui.PushID(99);
|
||||
ImGui.VSliderFloat("", box with { X = width }, ref model.MorphTime, 0.0f, 1.0f, "", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.PopID(); ImGui.PopStyleVar();
|
||||
ImGui.Spacing();
|
||||
ImGui.Text($"Time: {model.MorphTime:P}%");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(_errorColor, "mesh has no morph targets");
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
else NoMeshSelected();
|
||||
PopStyleCompact();
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTransform(Snooper s)
|
||||
private void DrawTransform(Model model, float speed)
|
||||
{
|
||||
if (ImGui.Begin("Transform"))
|
||||
const int width = 100;
|
||||
|
||||
ImGui.PushID(0); ImGui.BeginDisabled(model.TransformsCount < 2);
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.SliderInt("", ref model.SelectedInstance, 0, model.TransformsCount - 1, "Instance %i", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.EndDisabled(); ImGui.PopID();
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Location"))
|
||||
{
|
||||
PushStyleCompact();
|
||||
if (s.Renderer.Cache.Models.TryGetValue(s.Renderer.Settings.SelectedModel, out var model))
|
||||
ImGui.PushID(1);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Position.X, speed, 0f, 0f, "%.2f m");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Position.Z, speed, 0f, 0f, "%.2f m");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Position.Y, speed, 0f, 0f, "%.2f m");
|
||||
|
||||
ImGui.PopID();
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Rotation"))
|
||||
{
|
||||
ImGui.PushID(2);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Rotation.Roll, .5f, 0f, 0f, "%.1f°");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Rotation.Pitch, .5f, 0f, 0f, "%.1f°");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Rotation.Yaw, .5f, 0f, 0f, "%.1f°");
|
||||
|
||||
ImGui.PopID();
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (ImGui.TreeNode("Scale"))
|
||||
{
|
||||
ImGui.PushID(3);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Scale.X, speed, 0f, 0f, "%.3f");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Scale.Z, speed, 0f, 0f, "%.3f");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Scale.Y, speed, 0f, 0f, "%.3f");
|
||||
|
||||
ImGui.PopID();
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
model.UpdateMatrix(model.SelectedInstance);
|
||||
}
|
||||
|
||||
private void DrawUvChannels(Model model, Section section)
|
||||
{
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
|
||||
ImGui.PushID(0); ImGui.BeginDisabled(model.NumTexCoords < 2);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.SliderInt("", ref material.SelectedChannel, 0, model.NumTexCoords - 1, "Channel %i", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.EndDisabled(); ImGui.PopID();
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Textures"))
|
||||
{
|
||||
if (material.Diffuse.Length > 0)
|
||||
{
|
||||
const int width = 100;
|
||||
var speed = s.Camera.Speed / 100;
|
||||
|
||||
ImGui.PushID(0); ImGui.BeginDisabled(model.TransformsCount < 2);
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.SliderInt("", ref model.SelectedInstance, 0, model.TransformsCount - 1, "Instance %i", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.EndDisabled(); ImGui.PopID();
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Location"))
|
||||
{
|
||||
ImGui.PushID(1);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Position.X, speed, 0f, 0f, "%.2f m");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Position.Z, speed, 0f, 0f, "%.2f m");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Position.Y, speed, 0f, 0f, "%.2f m");
|
||||
|
||||
ImGui.PopID();
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Rotation"))
|
||||
{
|
||||
ImGui.PushID(2);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Rotation.Roll, .5f, 0f, 0f, "%.1f°");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Rotation.Pitch, .5f, 0f, 0f, "%.1f°");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Rotation.Yaw, .5f, 0f, 0f, "%.1f°");
|
||||
|
||||
ImGui.PopID();
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (ImGui.TreeNode("Scale"))
|
||||
{
|
||||
ImGui.PushID(3);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("X", ref model.Transforms[model.SelectedInstance].Scale.X, speed, 0f, 0f, "%.3f");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Y", ref model.Transforms[model.SelectedInstance].Scale.Z, speed, 0f, 0f, "%.3f");
|
||||
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.DragFloat("Z", ref model.Transforms[model.SelectedInstance].Scale.Y, speed, 0f, 0f, "%.3f");
|
||||
|
||||
ImGui.PopID();
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
model.UpdateMatrix(model.SelectedInstance);
|
||||
var size = new Vector2(ImGui.GetContentRegionAvail().X / 5.75f);
|
||||
DrawSquareTexture(material.Diffuse[material.SelectedChannel], size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.Normals[material.SelectedChannel], size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.SpecularMasks[material.SelectedChannel], size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.M.Texture, size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.Emissive[material.SelectedChannel], size); ImGui.SameLine();
|
||||
}
|
||||
else NoMeshSelected();
|
||||
PopStyleCompact();
|
||||
ImGui.End();
|
||||
else TextColored(_errorColor, "no texture in material section");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawUvChannels(Snooper s)
|
||||
private void DrawMaterials(Model model, Section section)
|
||||
{
|
||||
if (ImGui.Begin("UV Channels"))
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
foreach ((string key, float value) in material.Parameters.Scalars)
|
||||
{
|
||||
PushStyleCompact();
|
||||
if (s.Renderer.Cache.Models.TryGetValue(s.Renderer.Settings.SelectedModel, out var model) &&
|
||||
s.Renderer.Settings.TryGetSection(model, out var section))
|
||||
{
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
ImGui.Text($"{key}: {value}");
|
||||
}
|
||||
ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing();
|
||||
foreach ((string key, FLinearColor value) in material.Parameters.Colors)
|
||||
{
|
||||
ImGui.Text(key); ImGui.SameLine();
|
||||
ImGui.ColorButton(key, new Vector4(value.R, value.G, value.B, value.A));
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.PushID(0); ImGui.BeginDisabled(model.NumTexCoords < 2);
|
||||
ImGui.SetNextItemWidth(width);
|
||||
ImGui.SliderInt("", ref material.SelectedChannel, 0, model.NumTexCoords - 1, "Channel %i", ImGuiSliderFlags.AlwaysClamp);
|
||||
ImGui.EndDisabled(); ImGui.PopID();
|
||||
private void DrawTextures(Model model, Section section)
|
||||
{
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
foreach ((string key, UUnrealMaterial value) in material.Parameters.Textures)
|
||||
{
|
||||
ImGui.Text($"{key}: {value.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
if (ImGui.TreeNode("Textures"))
|
||||
{
|
||||
if (material.Diffuse.Length > 0)
|
||||
{
|
||||
var size = new Vector2(ImGui.GetContentRegionAvail().X / 5.75f);
|
||||
DrawSquareTexture(material.Diffuse[material.SelectedChannel], size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.Normals[material.SelectedChannel], size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.SpecularMasks[material.SelectedChannel], size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.M.Texture, size); ImGui.SameLine();
|
||||
DrawSquareTexture(material.Emissive[material.SelectedChannel], size); ImGui.SameLine();
|
||||
}
|
||||
else TextColored(_errorColor, "no texture in material section");
|
||||
}
|
||||
}
|
||||
else NoMeshSelected();
|
||||
PopStyleCompact();
|
||||
ImGui.End();
|
||||
private void DrawParameters(Model model, Section section)
|
||||
{
|
||||
const int width = 50;
|
||||
var material = model.Materials[section.MaterialIndex];
|
||||
ImGui.Checkbox("Show Section", ref section.Show);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("Emissive Multiplier", ref material.EmissiveMult, .01f, 0f);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("UV Scale", ref material.UVScale, .01f, 0f);
|
||||
if (material.HasM)
|
||||
{
|
||||
ImGui.ColorEdit3("Skin Boost Color", ref material.M.SkinBoost.Color, ImGuiColorEditFlags.NoInputs);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("Skin Boost Exponent", ref material.M.SkinBoost.Exponent, .01f, 0f);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("AmbientOcclusion", ref material.M.AmbientOcclusion, .01f, 0f, 1f);
|
||||
ImGui.SetNextItemWidth(width); ImGui.DragFloat("Cavity", ref material.M.Cavity, .01f, 0f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
private void Draw3DViewport(Snooper s)
|
||||
{
|
||||
const float lookSensitivity = 0.1f;
|
||||
const ImGuiWindowFlags flags =
|
||||
ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse |
|
||||
ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysUseWindowPadding;
|
||||
|
||||
// ImGui.SetNextWindowSize(_viewportSize, _firstUse);
|
||||
// ImGui.SetNextWindowPos(_viewportPosition, _firstUse);
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
|
||||
ImGui.Begin("3D Viewport", flags);
|
||||
Window("3D Viewport", () =>
|
||||
{
|
||||
var largest = ImGui.GetContentRegionAvail();
|
||||
largest.X -= ImGui.GetScrollX();
|
||||
largest.Y -= ImGui.GetScrollY();
|
||||
|
||||
var size = new Vector2(largest.X, largest.Y);
|
||||
s.Camera.AspectRatio = size.X / size.Y;
|
||||
ImGui.ImageButton(s.Framebuffer.GetPointer(), size, new Vector2(0, 1), new Vector2(1, 0), 0);
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
// if left button down while mouse is hover viewport
|
||||
if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && !_viewportFocus)
|
||||
{
|
||||
_viewportFocus = true;
|
||||
s.CursorState = CursorState.Grabbed;
|
||||
}
|
||||
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
var guid = s.Renderer.Picking.ReadPixel(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), size);
|
||||
s.Renderer.Settings.SelectModel(guid);
|
||||
ImGui.SetWindowFocus("Outliner");
|
||||
}
|
||||
}
|
||||
|
||||
const float lookSensitivity = 0.1f;
|
||||
if (ImGui.IsMouseDragging(ImGuiMouseButton.Left, lookSensitivity) && _viewportFocus)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
var delta = io.MouseDelta * lookSensitivity;
|
||||
s.Camera.ModifyDirection(delta.X, delta.Y);
|
||||
}
|
||||
|
||||
// if left button up and mouse was in viewport
|
||||
if (ImGui.IsMouseReleased(ImGuiMouseButton.Left) && _viewportFocus)
|
||||
{
|
||||
_viewportFocus = false;
|
||||
s.CursorState = CursorState.Normal;
|
||||
}
|
||||
|
||||
float framerate = ImGui.GetIO().Framerate;
|
||||
ImGui.SetCursorPos(size with { X = 7.5f });
|
||||
ImGui.Text($"FPS: {framerate:0} ({1000.0f / framerate:0.##} ms)");
|
||||
}, false);
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
var largest = ImGui.GetContentRegionAvail();
|
||||
largest.X -= ImGui.GetScrollX();
|
||||
largest.Y -= ImGui.GetScrollY();
|
||||
|
||||
var size = new Vector2(largest.X, largest.Y);
|
||||
s.Camera.AspectRatio = size.X / size.Y;
|
||||
ImGui.ImageButton(s.Framebuffer.GetPointer(), size, new Vector2(0, 1), new Vector2(1, 0), 0);
|
||||
|
||||
// it took me 5 hours to make it work, don't change any of the following code
|
||||
// basically the Raw cursor doesn't actually freeze the mouse position
|
||||
// so for ImGui, the IsItemHovered will be false if mouse leave, even in Raw mode
|
||||
if (ImGui.IsItemHovered())
|
||||
private void Window(string name, Action content, bool styled = true)
|
||||
{
|
||||
if (ImGui.Begin(name))
|
||||
{
|
||||
// if left button down while mouse is hover viewport
|
||||
if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && !_viewportFocus)
|
||||
{
|
||||
_viewportFocus = true;
|
||||
s.CursorState = CursorState.Grabbed;
|
||||
}
|
||||
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
var guid = s.Renderer.Picking.ReadPixel(ImGui.GetMousePos(), ImGui.GetCursorScreenPos(), size);
|
||||
s.Renderer.Settings.SelectModel(guid);
|
||||
ImGui.SetWindowFocus("Outliner");
|
||||
}
|
||||
if (styled) PushStyleCompact();
|
||||
content();
|
||||
if (styled) PopStyleCompact();
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
|
||||
// this can't be inside IsItemHovered! read it as
|
||||
// if left mouse button was pressed while hovering the viewport
|
||||
// move camera until left mouse button is released
|
||||
// no matter where mouse position end up
|
||||
if (ImGui.IsMouseDragging(ImGuiMouseButton.Left, lookSensitivity) && _viewportFocus)
|
||||
private void MeshWindow(string name, Renderer renderer, Action<Model> content, bool styled = true)
|
||||
{
|
||||
Window(name, () =>
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
var delta = io.MouseDelta * lookSensitivity;
|
||||
s.Camera.ModifyDirection(delta.X, delta.Y);
|
||||
}
|
||||
if (renderer.Cache.Models.TryGetValue(renderer.Settings.SelectedModel, out var model)) content(model);
|
||||
else NoMeshSelected();
|
||||
}, styled);
|
||||
}
|
||||
|
||||
// if left button up and mouse was in viewport
|
||||
if (ImGui.IsMouseReleased(ImGuiMouseButton.Left) && _viewportFocus)
|
||||
private void SectionWindow(string name, Renderer renderer, Action<Model, Section> content, bool styled = true)
|
||||
{
|
||||
MeshWindow(name, renderer, model =>
|
||||
{
|
||||
_viewportFocus = false;
|
||||
s.CursorState = CursorState.Normal;
|
||||
}
|
||||
|
||||
const float padding = 7.5f;
|
||||
float framerate = ImGui.GetIO().Framerate;
|
||||
var text = $"FPS: {framerate:0} ({1000.0f / framerate:0.##} ms)";
|
||||
ImGui.SetCursorPos(size with { X = padding });
|
||||
ImGui.Text(text);
|
||||
|
||||
ImGui.End();
|
||||
if (renderer.Settings.TryGetSection(model, out var section)) content(model, section);
|
||||
else NoSectionSelected();
|
||||
}, styled);
|
||||
}
|
||||
|
||||
private void PopStyleCompact() => ImGui.PopStyleVar(2);
|
||||
|
|
@ -561,6 +549,7 @@ public class SnimGui
|
|||
}
|
||||
|
||||
private void NoMeshSelected() => TextColored(_errorColor, "no mesh selected");
|
||||
private void NoSectionSelected() => TextColored(_errorColor, "no section selected");
|
||||
private void TextColored(Vector4 color, string text)
|
||||
{
|
||||
ImGui.TextColored(color, text);
|
||||
|
|
|
|||
26
FModel/Views/Snooper/Socket.cs
Normal file
26
FModel/Views/Snooper/Socket.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
|
||||
namespace FModel.Views.Snooper;
|
||||
|
||||
public class Socket : IDisposable
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly string Bone;
|
||||
public readonly Transform Transform;
|
||||
|
||||
public Socket(USkeletalMeshSocket socket, Transform relation)
|
||||
{
|
||||
Name = socket.SocketName.Text;
|
||||
Bone = socket.BoneName.Text;
|
||||
Transform = Transform.Identity;
|
||||
Transform.Relation = relation.Matrix;
|
||||
Transform.Position = socket.RelativeLocation.ToMapVector() * Constants.SCALE_DOWN_RATIO;
|
||||
Transform.Rotation = socket.RelativeRotation;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user