#region usings
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.Threading;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using SM.Base.Scene;
using SM.Base.Utility;
using SM.Base.Window.Contexts;
using SM.OGL;
using Mouse = SM.Base.Controls.Mouse;
#endregion
namespace SM.Base.Window
{
///
/// This provides the main entry, by executing the window.
///
public class GLWindow : GameWindow, IGenericWindow
{
private Vector2 _flagWindowPos;
private Vector2 _flagWindowSize;
private Thread _fixedUpdateThread;
private WindowFlags _windowFlags;
private DisplayResolution _normalResolution;
private DisplayResolution _fullscreenResolution;
///
public bool Loading { get; private set; } = true;
///
public float AspectRatio { get; set; }
///
public GenericCamera ViewportCamera { get; set; }
///
public bool ForceViewportCamera { get; set; }
///
public bool DrawWhileUnfocused { get; set; } = true;
///
public bool UpdateWhileUnfocused { get; set; } = false;
///
public Vector2 WindowSize { get; set; }
///
public ISetup AppliedSetup { get; private set; }
///
public new event Action Resize;
///
public new event Action Load;
///
public GenericScene CurrentScene { get; private set; }
///
public RenderPipeline CurrentRenderPipeline { get; private set; }
///
/// Gets/Sets the current window flag.
///
public WindowFlags WindowFlags
{
get => _windowFlags;
set
{
if (_windowFlags != value)
{
WindowFlags oldV = _windowFlags;
_windowFlags = value;
ChangeWindowFlag(value, oldV);
}
}
}
///
/// Loads the window with default values.
/// Width: 1280px; Height: 720px; Title: Generic OpenGL Title; WindowFlag: Window
///
public GLWindow() : this(1280, 720, "Generic OpenGL Title", WindowFlags.Window)
{
}
///
/// Loads the window with custom values.
///
///
///
///
///
///
public GLWindow(int width, int height, string title, WindowFlags flags, VSyncMode vSync = VSyncMode.On) :
base(width, height, default, title, GameWindowFlags.Default, DisplayDevice.Default,
GLSettings.ForcedVersion.MajorVersion, GLSettings.ForcedVersion.MinorVersion,
GraphicsContextFlags.Default)
{
VSync = vSync;
WindowFlags = flags;
FocusedChanged += GLWindow_FocusedChanged;
_normalResolution = _fullscreenResolution = DisplayDevice.Default.SelectResolution(DisplayDevice.Default.Width, DisplayDevice.Default.Height, DisplayDevice.Default.BitsPerPixel, DisplayDevice.Default.RefreshRate);
}
///
/// A event that gets executed when the window is done loading.
///
public event Action Loaded;
///
protected override void OnLoad(EventArgs e)
{
WindowCode.Load(this);
SMRenderer.CurrentWindow = this;
base.OnLoad(e);
}
///
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
WindowCode.Resize(this);
if (Loading)
{
Loading = false;
Loaded?.Invoke(this);
AppliedSetup?.Loaded(this);
OnLoaded();
}
}
///
/// This gets called, when the window completed with all initilization steps.
///
protected virtual void OnLoaded()
{
Icon ??= new Icon(AssemblyUtility.GetAssemblyStream("SM.Base.Window.winIcon.ico"));
}
///
protected override void OnUpdateFrame(FrameEventArgs e)
{
if (!Focused && !UpdateWhileUnfocused) return;
base.OnUpdateFrame(e);
WindowCode.Update(this, (float) e.Time);
}
///
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
WindowCode.Render(this, (float) e.Time);
SwapBuffers();
GLDebugging.CheckGLErrors();
}
///
protected override void OnMouseMove(MouseMoveEventArgs e)
{
base.OnMouseMove(e);
Mouse.MouseMoveEvent(e, this);
}
///
public void TriggerLoad()
{
Load?.Invoke(this);
}
///
public void TriggerResize()
{
Resize?.Invoke(this);
}
///
public void Update(UpdateContext context)
{
}
///
public void ApplySetup(ISetup setup)
{
AppliedSetup = setup;
setup.Applied(this);
}
///
public void SetScene(GenericScene scene)
{
if (Loading)
{
Loaded += window => SetScene(scene);
return;
}
WindowCode.PrepareScene(this, scene);
CurrentScene = scene;
}
///
public void SetRenderPipeline(RenderPipeline renderPipeline)
{
if (Loading)
{
Loaded += window => SetRenderPipeline(renderPipeline);
return;
}
WindowCode.PreparePipeline(this, renderPipeline);
CurrentRenderPipeline = renderPipeline;
}
///
/// Changes the resolution in fullscreen mode.
/// Can be executed before changing to fullscreen, but with no effect until changed.
///
/// The resolution you get from or
public void ChangeFullscreenResolution(DisplayResolution resolution)
{
_fullscreenResolution = resolution;
if (_windowFlags == WindowFlags.ExclusiveFullscreen) ApplyFullscreenResolution();
}
///
/// Starts the fixed update loop.
/// Need to get executed before can be used.
///
///
public void RunFixedUpdate(float updatesPerSecond)
{
Deltatime.FixedUpdateDelta = 1 / (float)updatesPerSecond;
_fixedUpdateThread = new Thread(ExecuteFixedUpdate);
_fixedUpdateThread.Start();
}
private void ExecuteFixedUpdate()
{
Stopwatch deltaStop = new Stopwatch();
while (!IsExiting)
{
deltaStop.Restart();
FixedUpdateContext context = new FixedUpdateContext();
CurrentScene?.FixedUpdate(context);
long delta = deltaStop.ElapsedMilliseconds;
int waitTime = Math.Max((int)(Deltatime.FixedUpdateDelta * 1000) - (int)delta, 0);
Thread.Sleep(waitTime);
}
}
private void GLWindow_FocusedChanged(object sender, EventArgs e)
{
if (_windowFlags == WindowFlags.ExclusiveFullscreen)
{
if (!Focused)
{
DisplayDevice.Default.ChangeResolution(_normalResolution);
WindowState = WindowState.Minimized;
}
else
{
ApplyFullscreenResolution();
}
}
}
private void ChangeWindowFlag(WindowFlags newFlag, WindowFlags oldFlag)
{
if (oldFlag == WindowFlags.Window)
{
_flagWindowSize = WindowSize;
_flagWindowPos = new Vector2(X, Y);
}
switch (newFlag)
{
case WindowFlags.Window:
DisplayDevice.Default.ChangeResolution(_normalResolution);
WindowState = WindowState.Normal;
Width = (int)_flagWindowSize.X;
Height = (int)_flagWindowSize.Y;
X = (int) _flagWindowPos.X;
Y = (int) _flagWindowPos.Y;
WindowBorder = WindowBorder.Resizable;
break;
case WindowFlags.BorderlessWindow:
DisplayDevice.Default.ChangeResolution(_normalResolution);
WindowState = WindowState.Maximized;
WindowBorder = WindowBorder.Hidden;
X = 0;
Y = 0;
Width = DisplayDevice.Default.Width;
Height = DisplayDevice.Default.Height;
break;
case WindowFlags.ExclusiveFullscreen:
WindowState = WindowState.Fullscreen;
ApplyFullscreenResolution();
break;
default:
throw new ArgumentOutOfRangeException(nameof(newFlag), newFlag, null);
}
}
private void ApplyFullscreenResolution()
{
DisplayDevice.Default.ChangeResolution(_fullscreenResolution);
Width = _fullscreenResolution.Width;
Height = _fullscreenResolution.Height;
}
}
}