camera mode

This commit is contained in:
4sval
2023-01-01 03:49:25 +01:00
parent 9a0e6aa6c6
commit 26f9b5b9ce
7 changed files with 198 additions and 66 deletions

View File

@@ -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;

View File

@@ -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
{

View File

@@ -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();

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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);