Skip to content

Commit 1e79797

Browse files
chore: add camera
1 parent 25f76b3 commit 1e79797

7 files changed

Lines changed: 187 additions & 15 deletions

File tree

FreeFrame/Camera.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using OpenTK.Mathematics;
2+
using System;
3+
4+
namespace FreeFrame
5+
{
6+
// This is the camera class as it could be set up after the tutorials on the website.
7+
// It is important to note there are a few ways you could have set up this camera.
8+
// For example, you could have also managed the player input inside the camera class,
9+
// and a lot of the properties could have been made into functions.
10+
11+
// TL;DR: This is just one of many ways in which we could have set up the camera.
12+
// Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
13+
public class Camera
14+
{
15+
// Those vectors are directions pointing outwards from the camera to define how it rotated.
16+
private Vector3 _front = -Vector3.UnitZ;
17+
18+
private Vector3 _up = Vector3.UnitY;
19+
20+
private Vector3 _right = Vector3.UnitX;
21+
22+
// Rotation around the X axis (radians)
23+
private float _pitch;
24+
25+
// Rotation around the Y axis (radians)
26+
private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
27+
28+
// The field of view of the camera (radians)
29+
private float _fov = MathHelper.PiOver2;
30+
31+
public Camera(Vector3 position, float aspectRatio)
32+
{
33+
Position = position;
34+
AspectRatio = aspectRatio;
35+
}
36+
37+
// The position of the camera
38+
public Vector3 Position { get; set; }
39+
40+
// This is simply the aspect ratio of the viewport, used for the projection matrix.
41+
public float AspectRatio { private get; set; }
42+
43+
public Vector3 Front => _front;
44+
45+
public Vector3 Up => _up;
46+
47+
public Vector3 Right => _right;
48+
49+
// We convert from degrees to radians as soon as the property is set to improve performance.
50+
public float Pitch
51+
{
52+
get => MathHelper.RadiansToDegrees(_pitch);
53+
set
54+
{
55+
// We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
56+
// of weird "bugs" when you are using euler angles for rotation.
57+
// If you want to read more about this you can try researching a topic called gimbal lock
58+
var angle = MathHelper.Clamp(value, -89f, 89f);
59+
_pitch = MathHelper.DegreesToRadians(angle);
60+
UpdateVectors();
61+
}
62+
}
63+
64+
// We convert from degrees to radians as soon as the property is set to improve performance.
65+
public float Yaw
66+
{
67+
get => MathHelper.RadiansToDegrees(_yaw);
68+
set
69+
{
70+
_yaw = MathHelper.DegreesToRadians(value);
71+
UpdateVectors();
72+
}
73+
}
74+
75+
// The field of view (FOV) is the vertical angle of the camera view.
76+
// This has been discussed more in depth in a previous tutorial,
77+
// but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
78+
// We convert from degrees to radians as soon as the property is set to improve performance.
79+
public float Fov
80+
{
81+
get => MathHelper.RadiansToDegrees(_fov);
82+
set
83+
{
84+
var angle = MathHelper.Clamp(value, 1f, 45f);
85+
_fov = MathHelper.DegreesToRadians(angle);
86+
}
87+
}
88+
89+
// Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
90+
public Matrix4 GetViewMatrix()
91+
{
92+
return Matrix4.LookAt(Position, Position + _front, _up);
93+
}
94+
95+
// Get the projection matrix using the same method we have used up until this point
96+
public Matrix4 GetProjectionMatrix()
97+
{
98+
return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
99+
}
100+
101+
// This function is going to update the direction vertices using some of the math learned in the web tutorials.
102+
private void UpdateVectors()
103+
{
104+
// First, the front matrix is calculated using some basic trigonometry.
105+
_front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
106+
_front.Y = MathF.Sin(_pitch);
107+
_front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
108+
109+
// We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
110+
_front = Vector3.Normalize(_front);
111+
112+
// Calculate both the right and the up vector using cross product.
113+
// Note that we are calculating the right from the global up; this behaviour might
114+
// not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
115+
_right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
116+
_up = Vector3.Normalize(Vector3.Cross(_right, _front));
117+
}
118+
}
119+
}

FreeFrame/Components/Shapes/SVGPolygon.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ public SVGPolygon(XmlReader reader)
2626
_points.Add((Convert.ToInt32(match.Groups[1].Value), Convert.ToInt32(match.Groups[2].Value)));
2727
}
2828

29-
public override void Draw(Vector2i clientSize) => throw new NotImplementedException();
30-
3129
public override List<Vector2i> GetSelectablePoints()
3230
{
3331
throw new NotImplementedException();

FreeFrame/Components/Shapes/Shape.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ public Shape()
4343
/// <summary>
4444
/// Trigge draw element through OpenGL context
4545
/// </summary>
46-
public virtual void Draw(Vector2i clientSize)
46+
public virtual void Draw(Vector2i clientSize, Camera camera)
4747
{
4848
//Console.WriteLine("Draw {0}, {1}, {2}", GetType().Name, Id, GetHashCode());
4949
foreach (Renderer vao in Vaos)
50-
vao.Draw(clientSize, Color, this);
50+
vao.Draw(clientSize, camera, Color, this);
5151
}
5252
public void DeleteObjects()
5353
{

FreeFrame/Renderer.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public Renderer(float[] vertices, uint[] indexes, PrimitiveType primitiveType, S
4747
_shader = new Shader("Shaders/shader.vert", "Shaders/rectangle.frag");
4848
ImplementObjects(vertices, indexes);
4949
}
50-
public void Draw(Vector2i clientSize, Color4 color)
50+
public void Draw(Vector2i clientSize, Camera camera, Color4 color)
5151
{
5252
_shader.Use();
5353

@@ -67,11 +67,17 @@ public void Draw(Vector2i clientSize, Color4 color)
6767
Matrix4 transform = Matrix4.Identity;
6868
_shader.SetUniformMat4(uTransformation, transform);
6969

70+
int uView = _shader.GetUniformLocation("u_View");
71+
_shader.SetUniformMat4(uView, camera.GetViewMatrix());
72+
73+
int uProjection = _shader.GetUniformLocation("u_Projection");
74+
_shader.SetUniformMat4(uProjection, camera.GetViewMatrix());
75+
7076
GL.BindVertexArray(VertexArrayObjectID);
7177

7278
GL.DrawElements(_primitiveType, _indexCount, DrawElementsType.UnsignedInt, 0);
7379
}
74-
public void Draw(Vector2i clientSize, Color4 color, Shape shape)
80+
public void Draw(Vector2i clientSize, Camera camera, Color4 color, Shape shape)
7581
{
7682
_shader.Use();
7783
// Applied projection matrix
@@ -90,6 +96,12 @@ public void Draw(Vector2i clientSize, Color4 color, Shape shape)
9096
Matrix4 rotation = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(shape.Angle));
9197
_shader.SetUniformMat4(uTransformation, rotation);
9298

99+
int uView = _shader.GetUniformLocation("u_View");
100+
_shader.SetUniformMat4(uView, camera.GetViewMatrix());
101+
102+
int uProjection = _shader.GetUniformLocation("u_Projection");
103+
_shader.SetUniformMat4(uProjection, camera.GetViewMatrix());
104+
93105

94106
Type type = shape.GetType();
95107
if (type == typeof(SVGCircle))

FreeFrame/Selector.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,16 @@ public static float[] AreaToFloatArray(Area area)
8484
area.X, area.Y, area.X + area.Width, area.Y, area.X + area.Width, area.Y + area.Height, area.X, area.Y + area.Height // Clockwise
8585
};
8686
}
87-
public void Draw(Vector2i clientSize)
87+
public void Draw(Vector2i clientSize, Camera camera)
8888
{
8989
GL.Enable(EnableCap.LineSmooth);
9090
GL.LineWidth(3.0f);
9191
foreach ((Renderer vao, Area _, SelectorType type) part in _vaos)
9292
{
9393
if (part.type == SelectorType.Edge)
94-
part.vao.Draw(clientSize, new Color4(0, 125, 200, 255));
94+
part.vao.Draw(clientSize, camera, new Color4(0, 125, 200, 255));
9595
else
96-
part.vao.Draw(clientSize, new Color4(0, 125, 255, 255));
96+
part.vao.Draw(clientSize, camera, new Color4(0, 125, 255, 255));
9797
}
9898
GL.Disable(EnableCap.LineSmooth);
9999

FreeFrame/Shaders/shader.vert

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ layout(location = 0) in vec2 position;
44

55
uniform mat4 u_Model_To_NDC;
66
uniform mat4 u_Transformation;
7+
uniform mat4 u_View;
8+
uniform mat4 u_Projection;
9+
710

811
void main()
912
{
10-
gl_Position = u_Transformation * (u_Model_To_NDC * vec4(position,1.0, 1.0));
13+
gl_Position = u_Transformation * (u_Model_To_NDC * vec4(position,1.0, 1.0)); // * u_View * u_Projection);
1114
}

FreeFrame/Window.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ enum UserMode
2424
{
2525
Idle,
2626
Edit,
27-
Create
27+
Create,
28+
Move
2829
}
2930
enum CreateMode
3031
{
@@ -42,10 +43,12 @@ enum CreateMode
4243
int _ioTimeline;
4344
bool _ioIsLoop;
4445
bool _ioIsReverse;
46+
private Camera _camera;
4547
bool _dialogFilePicker = false;
4648
bool _dialogCompatibility = false;
4749

4850
Vector2i _mouseOriginalState;
51+
Vector3i _cameraOriginalState;
4952

5053
SortedDictionary<int, List<Shape>> _timeline;
5154

@@ -81,11 +84,14 @@ protected override void OnLoad()
8184
_selector = new Selector();
8285

8386
_mouseOriginalState = new Vector2i(0, 0);
87+
_cameraOriginalState = new Vector3i(0, 0, 0);
8488

8589
_timeline = new SortedDictionary<int, List<Shape>>();
8690

8791
_ImGuiController = new ImGuiController(ClientSize.X, ClientSize.Y);
8892

93+
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
94+
8995
_ioIsLoop = false;
9096
_ioIsReverse = false;
9197

@@ -99,6 +105,8 @@ protected override void OnResize(ResizeEventArgs e)
99105
GL.Viewport(0, 0, ClientSize.X, ClientSize.Y);
100106

101107
_ImGuiController.WindowResized(ClientSize.X, ClientSize.Y);
108+
109+
_camera.AspectRatio = Size.X / (float)Size.Y; // TODO: Why not ClientSize
102110
}
103111
/// <summary>
104112
/// Triggered at a fixed interval. (Logic, etc.)
@@ -142,6 +150,16 @@ protected override void OnRenderFrame(FrameEventArgs e)
142150
// _selectedShape.ImplementObject(); // Reset VAOS for selected shape
143151
}
144152

153+
if (KeyboardState.IsKeyPressed(Keys.Right))
154+
{
155+
_camera.Position += Vector3.Normalize(Vector3.Cross(_camera.Front, _camera.Up)) * 2;
156+
}
157+
158+
//if (KeyboardState.IsKeyDown(Keys.Space))
159+
// _userMode = UserMode.Move;
160+
//else if (KeyboardState.WasKeyDown(Keys.Space) && KeyboardState.IsKeyDown(Keys.Space) == false)
161+
// _userMode = UserMode.Idle;
162+
145163
if (KeyboardState.IsKeyDown(Keys.Escape))
146164
ResetSelection();
147165

@@ -168,15 +186,14 @@ protected override void OnRenderFrame(FrameEventArgs e)
168186
_selectedShape = shape;
169187
}
170188
}
171-
189+
// Change to ButtonPressed instead of was and is
172190
if (MouseState.WasButtonDown(MouseButton.Left) == false && MouseState.IsButtonDown(MouseButton.Left) == true) // First left click
173191
OnLeftMouseDown();
174192
else if (MouseState.WasButtonDown(MouseButton.Left) == true && MouseState.IsButtonDown(MouseButton.Left) == true) // Long left click
175193
OnLeftMouseEnter();
176194
else if (MouseState.WasButtonDown(MouseButton.Left) == true && MouseState.IsButtonDown(MouseButton.Left) == false) // Release left click
177195
OnLeftMouseUp();
178196

179-
180197
//GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
181198
foreach (Shape shape in _shapes)
182199
{
@@ -186,7 +203,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
186203

187204
//shape.ImplementObject(); // Reset VAOs
188205
//shape.ImplementObject();
189-
shape.Draw(ClientSize);
206+
shape.Draw(ClientSize, _camera);
190207
}
191208

192209
if (_selectedShape != null)
@@ -240,10 +257,11 @@ protected override void OnRenderFrame(FrameEventArgs e)
240257
switch (_userMode)
241258
{
242259
case UserMode.Edit:
243-
_selector.Draw(ClientSize); // Only draw selector on edit mode
260+
_selector.Draw(ClientSize, _camera); // Only draw selector on edit mode
244261
break;
245262
case UserMode.Create:
246263
break;
264+
case UserMode.Move:
247265
case UserMode.Idle:
248266
default:
249267
break;
@@ -381,6 +399,18 @@ public void OnLeftMouseDown()
381399
}
382400
break;
383401
case UserMode.Create:
402+
case UserMode.Move:
403+
//if (ImGui.GetIO().WantCaptureMouse == false) // If it's not ImGui click
404+
//{
405+
// _mouseOriginalState.X = (int)MouseState.X;
406+
// _mouseOriginalState.Y = (int)MouseState.Y;
407+
408+
// _cameraOriginalState.X = (int)_camera.Position.X;
409+
// _cameraOriginalState.Y = (int)_camera.Position.Y;
410+
// _cameraOriginalState.Z = (int)_camera.Position.Z;
411+
412+
//}
413+
//break;
384414
default:
385415
break;
386416
}
@@ -464,6 +494,9 @@ public void OnLeftMouseEnter() // TODO: Rename this
464494
}
465495
}
466496
break;
497+
case UserMode.Move:
498+
//_camera.Position = new Vector3(_cameraOriginalState.X + (-MouseState.X / Size.X) - (-_mouseOriginalState.X / Size.X), _cameraOriginalState.Y + (MouseState.Y / Size.Y) - (_mouseOriginalState.Y / Size.Y), _camera.Position.Z);
499+
break;
467500
case UserMode.Idle:
468501
default:
469502
break;
@@ -561,6 +594,13 @@ public void ShowUIDebug()
561594
}
562595
ImGui.End();
563596
}
597+
598+
protected override void OnMouseWheel(MouseWheelEventArgs e)
599+
{
600+
base.OnMouseWheel(e);
601+
//Console.WriteLine(_camera.Position.Z);
602+
_camera.Position = new Vector3(_camera.Position.X, _camera.Position.Y, _camera.Position.Z + (1f * -e.OffsetY)); // TODO: Smooth the scroll when close
603+
}
564604
public int LinearInterpolate(int x, int x1, int x2, int y1, int y2)
565605
{
566606
float y;

0 commit comments

Comments
 (0)