Skip to content

Commit

Permalink
camera mode
Browse files Browse the repository at this point in the history
  • Loading branch information
4sval committed Jan 1, 2023
1 parent 9a0e6aa commit 26f9b5b
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 65 deletions.
1 change: 0 additions & 1 deletion FModel/Resources/default.frag
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions FModel/Settings/UserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using FModel.Framework;
using FModel.ViewModels;
using FModel.ViewModels.ApiEndpoints.Models;
using FModel.Views.Snooper;
using Newtonsoft.Json;

namespace FModel.Settings
Expand Down Expand Up @@ -614,6 +615,13 @@ public bool ShowGrid
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
{
Expand Down
190 changes: 164 additions & 26 deletions FModel/Views/Snooper/Camera.cs
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -26,51 +37,173 @@ public 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)
{
//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);
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 ModifyDirection(float xOffset, float yOffset)
public void Modify(KeyboardState keyboard, float time)
{
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);
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 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()
Expand All @@ -84,13 +217,18 @@ public Matrix4x4 GetProjectionMatrix()
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();

Expand Down
14 changes: 12 additions & 2 deletions FModel/Views/Snooper/Models/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -68,12 +70,20 @@ protected Model(UObject export)
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;
Expand Down
16 changes: 6 additions & 10 deletions FModel/Views/Snooper/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,7 @@ public void Render(Camera cam)
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)
Expand All @@ -165,7 +161,7 @@ private Camera LoadStaticMesh(UStaticMesh original)

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)
Expand All @@ -175,7 +171,7 @@ private Camera LoadSkeletalMesh(USkeletalMesh original)

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)
Expand All @@ -185,12 +181,12 @@ private Camera LoadMaterialInstance(UMaterialInstance original)

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;

Expand Down Expand Up @@ -224,7 +220,7 @@ private void WorldCamera(UObject actor, ref Camera cam)
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)
Expand Down
9 changes: 3 additions & 6 deletions FModel/Views/Snooper/SnimGui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ private void DrawOuliner(Snooper s)
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);
Expand Down Expand Up @@ -672,12 +672,9 @@ private void Draw3DViewport(Snooper s)
}
}
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
Expand Down
Loading

0 comments on commit 26f9b5b

Please sign in to comment.