mirror of
https://github.com/4sval/FModel.git
synced 2026-09-12 19:46:47 -05:00
camera mode
This commit is contained in:
@@ -241,7 +241,6 @@ void main()
|
||||
result = mix(result, result * color, m.b);
|
||||
}
|
||||
result = vec3(uParameters.Ao.AmbientOcclusion) * result * m.r;
|
||||
result += CalcLight(layer, normals, vec3(0.0), vec3(0.25), m.g, false);
|
||||
}
|
||||
|
||||
vec2 coords = fTexCoords;
|
||||
|
||||
@@ -12,6 +12,7 @@ using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using FModel.Framework;
|
||||
using FModel.ViewModels;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
using FModel.Views.Snooper;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FModel.Settings
|
||||
@@ -614,6 +615,13 @@ namespace FModel.Settings
|
||||
set => SetProperty(ref _showGrid, value);
|
||||
}
|
||||
|
||||
private Camera.WorldMode _cameraMode = Camera.WorldMode.Arcball;
|
||||
public Camera.WorldMode CameraMode
|
||||
{
|
||||
get => _cameraMode;
|
||||
set => SetProperty(ref _cameraMode, value);
|
||||
}
|
||||
|
||||
private bool _previewStaticMeshes = true;
|
||||
public bool PreviewStaticMeshes
|
||||
{
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using FModel.Settings;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
|
||||
namespace FModel.Views.Snooper;
|
||||
|
||||
public class Camera
|
||||
{
|
||||
public enum WorldMode
|
||||
{
|
||||
FlyCam,
|
||||
Arcball
|
||||
}
|
||||
|
||||
public Vector3 Position;
|
||||
public Vector3 Direction;
|
||||
public Vector3 Focus => Position - Direction;
|
||||
public Vector3 Up = Vector3.UnitY;
|
||||
public WorldMode Mode = UserSettings.Default.CameraMode;
|
||||
|
||||
public float Yaw = -90f;
|
||||
public float Pitch = 0f;
|
||||
public float Zoom = 60f;
|
||||
public float Speed = 1f;
|
||||
public float Near = 0.01f;
|
||||
public float Far = 100f;
|
||||
public float Near => 0.01f;
|
||||
public float AspectRatio = 16f / 9f;
|
||||
|
||||
public Camera()
|
||||
@@ -26,51 +37,173 @@ public class Camera
|
||||
InitDirection();
|
||||
}
|
||||
|
||||
public Camera(Vector3 position, Vector3 direction, float near, float far, float speed)
|
||||
public Camera(FBox box, float far)
|
||||
{
|
||||
Far = far;
|
||||
Teleport(FVector.ZeroVector, box, true);
|
||||
}
|
||||
|
||||
public Camera(Vector3 position, Vector3 direction, float far, float speed)
|
||||
{
|
||||
Position = position;
|
||||
Direction = direction;
|
||||
Near = near;
|
||||
Far = far;
|
||||
Speed = speed;
|
||||
|
||||
InitDirection();
|
||||
}
|
||||
|
||||
public void Teleport(FVector instancePos, FBox box, bool updateSpeed = false)
|
||||
{
|
||||
box.GetCenterAndExtents(out var center, out var extents);
|
||||
center = center.ToMapVector();
|
||||
center += instancePos;
|
||||
var distance = extents.AbsMax();
|
||||
|
||||
Position = new Vector3(instancePos.X, center.Y, instancePos.Z + distance * 2);
|
||||
Direction = new Vector3(center.X, center.Y, center.Z);
|
||||
if (updateSpeed) Speed = distance;
|
||||
|
||||
InitDirection();
|
||||
}
|
||||
|
||||
private void InitDirection()
|
||||
{
|
||||
// trigonometric math to calculate the cam's yaw/pitch based on position and direction to look
|
||||
var yaw = MathF.Atan((-Position.X - Direction.X) / (Position.Z - Direction.Z));
|
||||
var pitch = MathF.Atan((Position.Y - Direction.Y) / (Position.Z - Direction.Z));
|
||||
ModifyDirection(Helper.RadiansToDegrees(yaw), Helper.RadiansToDegrees(pitch));
|
||||
Modify(Helper.RadiansToDegrees(yaw), Helper.RadiansToDegrees(pitch));
|
||||
}
|
||||
|
||||
public void ModifyZoom(float zoomAmount)
|
||||
public void Modify(Vector2 mouseDelta)
|
||||
{
|
||||
var lookSensitivity = Mode switch
|
||||
{
|
||||
WorldMode.FlyCam => 0.1f,
|
||||
WorldMode.Arcball => 0.01f,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
mouseDelta *= lookSensitivity;
|
||||
Modify(mouseDelta.X, mouseDelta.Y);
|
||||
}
|
||||
private void Modify(float xOffset, float yOffset)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case WorldMode.FlyCam:
|
||||
{
|
||||
Yaw += xOffset;
|
||||
Pitch -= yOffset;
|
||||
Pitch = Math.Clamp(Pitch, -89f, 89f);
|
||||
|
||||
var direction = Vector3.Zero;
|
||||
var yaw = Helper.DegreesToRadians(Yaw);
|
||||
var pitch = Helper.DegreesToRadians(Pitch);
|
||||
direction.X = MathF.Cos(yaw) * MathF.Cos(pitch);
|
||||
direction.Y = MathF.Sin(pitch);
|
||||
direction.Z = MathF.Sin(yaw) * MathF.Cos(pitch);
|
||||
Direction = Vector3.Normalize(direction);
|
||||
break;
|
||||
}
|
||||
case WorldMode.Arcball:
|
||||
{
|
||||
var up = -Up;
|
||||
var rotationX = Matrix4x4.CreateFromAxisAngle(up, xOffset);
|
||||
Position = Vector3.Transform(Focus, rotationX) + Direction;
|
||||
|
||||
var right = Vector3.Normalize(Vector3.Cross(up, Focus));
|
||||
var rotationY = Matrix4x4.CreateFromAxisAngle(right, yOffset);
|
||||
Position = Vector3.Transform(Focus, rotationY) + Direction;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Modify(KeyboardState keyboard, float time)
|
||||
{
|
||||
var multiplier = keyboard.IsKeyDown(Keys.LeftShift) ? 2f : 1f;
|
||||
var moveSpeed = Speed * multiplier * time;
|
||||
|
||||
var focus = Mode switch
|
||||
{
|
||||
WorldMode.FlyCam => Direction,
|
||||
WorldMode.Arcball => -Focus,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
if (keyboard.IsKeyDown(Keys.W))
|
||||
Position += moveSpeed * focus;
|
||||
if (keyboard.IsKeyDown(Keys.S))
|
||||
Position -= moveSpeed * focus;
|
||||
|
||||
switch (Mode)
|
||||
{
|
||||
case WorldMode.FlyCam:
|
||||
{
|
||||
if (keyboard.IsKeyDown(Keys.A))
|
||||
Position -= Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed;
|
||||
if (keyboard.IsKeyDown(Keys.D))
|
||||
Position += Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed;
|
||||
if (keyboard.IsKeyDown(Keys.E))
|
||||
Position += moveSpeed * Up;
|
||||
if (keyboard.IsKeyDown(Keys.Q))
|
||||
Position -= moveSpeed * Up;
|
||||
break;
|
||||
}
|
||||
case WorldMode.Arcball:
|
||||
{
|
||||
if (keyboard.IsKeyDown(Keys.A))
|
||||
{
|
||||
var d = Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed;
|
||||
Position -= d;
|
||||
Direction -= d;
|
||||
}
|
||||
if (keyboard.IsKeyDown(Keys.D))
|
||||
{
|
||||
var d = Vector3.Normalize(Vector3.Cross(focus, Up)) * moveSpeed;
|
||||
Position += d;
|
||||
Direction += d;
|
||||
}
|
||||
if (keyboard.IsKeyDown(Keys.E))
|
||||
{
|
||||
var d = moveSpeed * Up;
|
||||
Position += d;
|
||||
Direction += d;
|
||||
}
|
||||
if (keyboard.IsKeyDown(Keys.Q))
|
||||
{
|
||||
var d = moveSpeed * Up;
|
||||
Position -= d;
|
||||
Direction -= d;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (keyboard.IsKeyDown(Keys.X))
|
||||
ModifyZoom(-.5f);
|
||||
if (keyboard.IsKeyDown(Keys.C))
|
||||
ModifyZoom(+.5f);
|
||||
}
|
||||
|
||||
private void ModifyZoom(float zoomAmount)
|
||||
{
|
||||
//We don't want to be able to zoom in too close or too far away so clamp to these values
|
||||
Zoom = Math.Clamp(Zoom - zoomAmount, 1.0f, 89f);
|
||||
}
|
||||
|
||||
public void ModifyDirection(float xOffset, float yOffset)
|
||||
{
|
||||
Yaw += xOffset;
|
||||
Pitch -= yOffset;
|
||||
|
||||
//We don't want to be able to look behind us by going over our head or under our feet so make sure it stays within these bounds
|
||||
Pitch = Math.Clamp(Pitch, -89f, 89f);
|
||||
|
||||
var direction = Vector3.Zero;
|
||||
var yaw = Helper.DegreesToRadians(Yaw);
|
||||
var pitch = Helper.DegreesToRadians(Pitch);
|
||||
direction.X = MathF.Cos(yaw) * MathF.Cos(pitch);
|
||||
direction.Y = MathF.Sin(pitch);
|
||||
direction.Z = MathF.Sin(yaw) * MathF.Cos(pitch);
|
||||
Direction = Vector3.Normalize(direction);
|
||||
}
|
||||
|
||||
public Matrix4x4 GetViewMatrix()
|
||||
{
|
||||
return Matrix4x4.CreateLookAt(Position, Position + Direction, Up);
|
||||
return Mode switch
|
||||
{
|
||||
WorldMode.FlyCam => Matrix4x4.CreateLookAt(Position, Position + Direction, Up),
|
||||
WorldMode.Arcball => Matrix4x4.CreateLookAt(Position, Direction, Up),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public Matrix4x4 GetProjectionMatrix()
|
||||
@@ -84,13 +217,18 @@ public class Camera
|
||||
private const ImGuiSliderFlags _clamp = ImGuiSliderFlags.AlwaysClamp;
|
||||
public void ImGuiCamera()
|
||||
{
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new System.Numerics.Vector2(8, 3));
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new System.Numerics.Vector2(0, 1));
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(8, 3));
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(0, 1));
|
||||
if (ImGui.BeginTable("camera_editor", 2))
|
||||
{
|
||||
SnimGui.Layout("Speed");ImGui.PushID(1);
|
||||
SnimGui.Layout("Mode");
|
||||
ImGui.PushID(1);var m = (int) Mode;
|
||||
ImGui.Combo("world_mode", ref m, "Fly Cam\0Arcball\0");
|
||||
Mode = (WorldMode) m;ImGui.PopID();
|
||||
|
||||
SnimGui.Layout("Speed");ImGui.PushID(2);
|
||||
ImGui.DragFloat("", ref Speed, _step, _zero, _infinite, "%.2f m/s", _clamp);
|
||||
ImGui.PopID();SnimGui.Layout("Far Plane");ImGui.PushID(2);
|
||||
ImGui.PopID();SnimGui.Layout("Far Plane");ImGui.PushID(3);
|
||||
ImGui.DragFloat("", ref Far, 0.1f, 0.1f, Far * 2f, "%.2f m", _clamp);
|
||||
ImGui.PopID();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
@@ -41,6 +42,7 @@ public class Model : IDisposable
|
||||
public readonly bool HasVertexColors;
|
||||
public readonly bool HasMorphTargets;
|
||||
public readonly int UvCount;
|
||||
public readonly FBox Box;
|
||||
public uint[] Indices;
|
||||
public float[] Vertices;
|
||||
public Section[] Sections;
|
||||
@@ -68,12 +70,20 @@ public class Model : IDisposable
|
||||
Name = Path.SubstringAfterLast('/').SubstringBefore('.');
|
||||
Type = export.ExportType;
|
||||
UvCount = 1;
|
||||
Box = new FBox(new FVector(-.65f), new FVector(.65f));
|
||||
Transforms = new List<Transform>();
|
||||
}
|
||||
|
||||
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) {}
|
||||
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) {}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
public Model(USkeletalMesh export, CSkeletalMesh skeletalMesh) : this(export, skeletalMesh, Transform.Identity)
|
||||
{
|
||||
var morphTargets = export.MorphTargets;
|
||||
|
||||
@@ -143,11 +143,7 @@ public class Renderer : IDisposable
|
||||
private Camera SetupCamera(FBox box)
|
||||
{
|
||||
var far = box.Max.AbsMax();
|
||||
var center = box.GetCenter();
|
||||
return new Camera(
|
||||
new Vector3(0f, center.Z, box.Max.Y * 3),
|
||||
new Vector3(center.X, center.Z, center.Y),
|
||||
0.01f, far * 50f, far / 1.5f);
|
||||
return new Camera(box, far * 50f);
|
||||
}
|
||||
|
||||
private Camera LoadStaticMesh(UStaticMesh original)
|
||||
@@ -165,7 +161,7 @@ public class Renderer : IDisposable
|
||||
|
||||
Options.Models[guid] = new Model(original, mesh);
|
||||
Options.SelectModel(guid);
|
||||
return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
|
||||
return SetupCamera(Options.Models[guid].Box);
|
||||
}
|
||||
|
||||
private Camera LoadSkeletalMesh(USkeletalMesh original)
|
||||
@@ -175,7 +171,7 @@ public class Renderer : IDisposable
|
||||
|
||||
Options.Models[guid] = new Model(original, mesh);
|
||||
Options.SelectModel(guid);
|
||||
return SetupCamera(mesh.BoundingBox *= Constants.SCALE_DOWN_RATIO);
|
||||
return SetupCamera(Options.Models[guid].Box);
|
||||
}
|
||||
|
||||
private Camera LoadMaterialInstance(UMaterialInstance original)
|
||||
@@ -185,12 +181,12 @@ public class Renderer : IDisposable
|
||||
|
||||
Options.Models[guid] = new Cube(original);
|
||||
Options.SelectModel(guid);
|
||||
return SetupCamera(new FBox(new FVector(-.65f), new FVector(.65f)));
|
||||
return SetupCamera(Options.Models[guid].Box);
|
||||
}
|
||||
|
||||
private Camera LoadWorld(CancellationToken cancellationToken, UWorld original, Transform transform)
|
||||
{
|
||||
var cam = new Camera(new Vector3(0f, 5f, 5f), Vector3.Zero, 0.01f, 1000f, 5f);
|
||||
var cam = new Camera(new Vector3(0f, 5f, 5f), Vector3.Zero, 1000f, 5f);
|
||||
if (original.PersistentLevel.Load<ULevel>() is not { } persistentLevel)
|
||||
return cam;
|
||||
|
||||
@@ -224,7 +220,7 @@ public class Renderer : IDisposable
|
||||
cam = new Camera(
|
||||
new Vector3(position.X, position.Y, position.Z),
|
||||
new Vector3(direction.X, direction.Y, direction.Z),
|
||||
0.01f, far * 25f, Math.Max(5f, far / 10f));
|
||||
far * 25f, Math.Max(5f, far / 10f));
|
||||
}
|
||||
|
||||
private void WorldLight(UObject actor)
|
||||
|
||||
@@ -351,7 +351,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
||||
if (ImGui.Selectable("Teleport To"))
|
||||
{
|
||||
var instancePos = model.Transforms[model.SelectedInstance].Position;
|
||||
s.Camera.Position = new Vector3(instancePos.X, instancePos.Y, instancePos.Z);
|
||||
s.Camera.Teleport(instancePos, model.Box);
|
||||
}
|
||||
|
||||
if (ImGui.Selectable("Delete")) s.Renderer.Options.Models.Remove(guid);
|
||||
@@ -672,12 +672,9 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
||||
}
|
||||
}
|
||||
|
||||
const float lookSensitivity = 0.1f;
|
||||
if (ImGui.IsMouseDragging(ImGuiMouseButton.Left, lookSensitivity) && _viewportFocus)
|
||||
if (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && _viewportFocus)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
var delta = io.MouseDelta * lookSensitivity;
|
||||
s.Camera.ModifyDirection(delta.X, delta.Y);
|
||||
s.Camera.Modify(ImGui.GetIO().MouseDelta);
|
||||
}
|
||||
|
||||
// if left button up and mouse was in viewport
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Snooper.Buffers;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
@@ -61,6 +61,7 @@ public class Snooper : GameWindow
|
||||
Renderer.Save();
|
||||
}
|
||||
|
||||
UserSettings.Default.CameraMode = Camera.Mode;
|
||||
GLFW.SetWindowShouldClose(WindowPtr, value); // start / stop game loop
|
||||
IsVisible = !value;
|
||||
}
|
||||
@@ -82,7 +83,7 @@ public class Snooper : GameWindow
|
||||
|
||||
private unsafe void LoadWindowIcon()
|
||||
{
|
||||
var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/engine.png", UriKind.Relative));
|
||||
var info = Application.GetResourceStream(new Uri("/FModel;component/Resources/engine.png", UriKind.Relative));
|
||||
using var img = SixLabors.ImageSharp.Image.Load<Rgba32>(info.Stream);
|
||||
var memoryGroup = img.GetPixelMemoryGroup();
|
||||
Memory<byte> array = new byte[memoryGroup.TotalLength * sizeof(Rgba32)];
|
||||
@@ -162,24 +163,7 @@ public class Snooper : GameWindow
|
||||
if (!IsVisible || ImGui.GetIO().WantTextInput)
|
||||
return;
|
||||
|
||||
var multiplier = KeyboardState.IsKeyDown(Keys.LeftShift) ? 2f : 1f;
|
||||
var moveSpeed = Camera.Speed * multiplier * (float) e.Time;
|
||||
if (KeyboardState.IsKeyDown(Keys.W))
|
||||
Camera.Position += moveSpeed * Camera.Direction;
|
||||
if (KeyboardState.IsKeyDown(Keys.S))
|
||||
Camera.Position -= moveSpeed * Camera.Direction;
|
||||
if (KeyboardState.IsKeyDown(Keys.A))
|
||||
Camera.Position -= Vector3.Normalize(Vector3.Cross(Camera.Direction, Camera.Up)) * moveSpeed;
|
||||
if (KeyboardState.IsKeyDown(Keys.D))
|
||||
Camera.Position += Vector3.Normalize(Vector3.Cross(Camera.Direction, Camera.Up)) * moveSpeed;
|
||||
if (KeyboardState.IsKeyDown(Keys.E))
|
||||
Camera.Position += moveSpeed * Camera.Up;
|
||||
if (KeyboardState.IsKeyDown(Keys.Q))
|
||||
Camera.Position -= moveSpeed * Camera.Up;
|
||||
if (KeyboardState.IsKeyDown(Keys.X))
|
||||
Camera.ModifyZoom(-.5f);
|
||||
if (KeyboardState.IsKeyDown(Keys.C))
|
||||
Camera.ModifyZoom(+.5f);
|
||||
Camera.Modify(KeyboardState, (float) e.Time);
|
||||
|
||||
if (KeyboardState.IsKeyPressed(Keys.H))
|
||||
WindowShouldClose(true, false);
|
||||
|
||||
Reference in New Issue
Block a user