Changed engine OpenGL version to 4.1. Added OpenGL program pipeline handles. Added fragment shaders. Some class name changes.
361 lines
14 KiB
C#
361 lines
14 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
using SDL2;
|
|
using SlatedGameToolkit.Framework.DataTypes;
|
|
using SlatedGameToolkit.Framework.Exceptions;
|
|
using SlatedGameToolkit.Framework.Graphics.OpenGL;
|
|
|
|
namespace SlatedGameToolkit.Framework.Graphics.Window
|
|
{
|
|
/// <summary>
|
|
/// A delegate to determine the type of interaction with a window.
|
|
/// The method should be extremely lightweight as it can be called many times.
|
|
/// </summary>
|
|
/// <param name="hitPoint">The window coordinates that are being interacted with.</param>
|
|
/// <returns>The region type.</returns>
|
|
public delegate WindowRegion WindowRegionHit(FloatVector2 hitPoint);
|
|
|
|
/// <summary>
|
|
/// A delegate that represents a function to be called on a window resize.
|
|
/// </summary>
|
|
/// <param name="width">The new width.</param>
|
|
/// <param name="height">The new height.</param>
|
|
public delegate void WindowResize(int width, int height);
|
|
|
|
/// <summary>
|
|
/// A delegate that represents a function to be called when a window event occurs.
|
|
/// </summary>
|
|
public delegate void WindowOperation();
|
|
|
|
/// <summary>
|
|
/// A handle for a window.
|
|
/// This object itself is not thread safe.
|
|
/// </summary>
|
|
public sealed class WindowContext : IDisposable
|
|
{
|
|
/// <summary>
|
|
/// The pointer referencing the SDL window.
|
|
/// </summary>
|
|
private readonly IntPtr window;
|
|
|
|
/// <summary>
|
|
/// The pointer referencing the OpenGL context.
|
|
/// </summary>
|
|
private readonly IntPtr glContext;
|
|
public event WindowOperation focusGainedEvent, focusLostEvent;
|
|
|
|
/// <summary>
|
|
/// Invoked when this window resizes.
|
|
/// </summary>
|
|
public event WindowResize resizeEvent;
|
|
|
|
/// <summary>
|
|
/// Event for when the window is being interacted with.
|
|
/// </summary>
|
|
public event WindowRegionHit windowRegionHitEvent;
|
|
|
|
internal readonly GLClearColour clearColour;
|
|
internal readonly GLClear clear;
|
|
internal readonly GLViewport viewport;
|
|
|
|
|
|
/// <summary>
|
|
/// Whether or not to show this window.
|
|
/// </summary>
|
|
/// <value>True for showing.</value>
|
|
public bool Shown {
|
|
set {
|
|
if (value) {
|
|
SDL.SDL_ShowWindow(window);
|
|
} else {
|
|
SDL.SDL_HideWindow(window);
|
|
}
|
|
}
|
|
|
|
get {
|
|
return ((SDL.SDL_WindowFlags) Enum.Parse(typeof(SDL.SDL_WindowFlags), SDL.SDL_GetWindowFlags(window).ToString())).HasFlag(SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether or not to attempt to perform vertical synchronization.
|
|
/// Will use adaptive sync if supported.
|
|
/// If this window is not the current active window, retrieving and setting requires a context switch.
|
|
/// If a context switch was nessecary, will automatically switch back to previous once operation is complete.
|
|
/// If neither normal VSync or adaptive sync are available, enabling will throw an OptionalSDLException.
|
|
/// </summary>
|
|
/// <value></value>
|
|
public bool VSync {
|
|
get {
|
|
IntPtr currentContext = SDL.SDL_GL_GetCurrentContext();
|
|
IntPtr currentWindow = SDL.SDL_GL_GetCurrentWindow();
|
|
bool diff = false;
|
|
if (currentContext != glContext || currentWindow != window) {
|
|
MakeCurrent();
|
|
diff = true;
|
|
}
|
|
bool vSync = SDL.SDL_GL_GetSwapInterval() != 0;
|
|
if (diff) SDL.SDL_GL_MakeCurrent(currentWindow, currentContext);
|
|
return vSync;
|
|
}
|
|
set {
|
|
IntPtr currentContext = SDL.SDL_GL_GetCurrentContext();
|
|
IntPtr currentWindow = SDL.SDL_GL_GetCurrentWindow();
|
|
bool diff = false;
|
|
if (currentContext != glContext || currentWindow != window) {
|
|
MakeCurrent();
|
|
diff = true;
|
|
}
|
|
if (SDL.SDL_GL_SetSwapInterval(value ? -1 : 0) < 0) {
|
|
if (SDL.SDL_GL_SetSwapInterval(value ? 1 : 0) < 0) {
|
|
throw new OptionalSDLException();
|
|
}
|
|
}
|
|
if (diff) SDL.SDL_GL_MakeCurrent(currentWindow, currentContext);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The title displayed on the window.
|
|
/// </summary>
|
|
/// <value>A string that represents whats to be displayed as the title of the window.</value>
|
|
public string WindowTitle {
|
|
set {
|
|
SDL.SDL_SetWindowTitle(window, value);
|
|
}
|
|
|
|
get {
|
|
return SDL.SDL_GetWindowTitle(window);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether or not to display the default window border.
|
|
/// </summary>
|
|
/// <value>True if displaying borders.</value>
|
|
public bool WindowBordered {
|
|
set {
|
|
SDL.SDL_SetWindowBordered(window, value ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE);
|
|
}
|
|
|
|
get {
|
|
int top, bottom, left, right;
|
|
int errorCode = SDL.SDL_GetWindowBordersSize(window, out top, out left, out bottom, out right);
|
|
if (errorCode < 0) throw new OptionalSDLException();
|
|
return top > 0 || bottom > 0 || left > 0 || right > 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether or not this window should be resizeable.
|
|
/// </summary>
|
|
/// <value>True if resizeable.</value>
|
|
public bool WindowResizable {
|
|
set {
|
|
SDL.SDL_SetWindowResizable(window, value ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE);
|
|
}
|
|
get {
|
|
return ((SDL.SDL_WindowFlags) Enum.Parse(typeof(SDL.SDL_WindowFlags), SDL.SDL_GetWindowFlags(window).ToString())).HasFlag(SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The boundaries of the window represent both the bottom left corner relative to the screen,
|
|
/// as well as the current width and height of this window.
|
|
/// </summary>
|
|
/// <value>Rectangle representing the boundaries of the window.</value>
|
|
public FloatRectangle WindowBoundaries {
|
|
set {
|
|
SDL.SDL_SetWindowPosition(window, (int)value.X1, (int)value.Y1);
|
|
SDL.SDL_SetWindowSize(window, (int)value.Width, (int)value.Height);
|
|
}
|
|
get {
|
|
int x, y, width, height;
|
|
SDL.SDL_GetWindowSize(window, out x, out y);
|
|
SDL.SDL_GetWindowPosition(window, out width, out height);
|
|
FloatRectangle rectangle = new FloatRectangle();
|
|
rectangle.X1 = x;
|
|
rectangle.Y2 = y;
|
|
rectangle.Width = width;
|
|
rectangle.Height = height;
|
|
|
|
return rectangle;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The maximum size the window can be at any given time.
|
|
/// </summary>
|
|
/// <value>A vector that represents the maximum width and height of the window with the x and y components respectively.</value>
|
|
public FloatVector2 MaximumSize {
|
|
set {
|
|
SDL.SDL_SetWindowMaximumSize(window, (int)value.x, (int)value.y);
|
|
}
|
|
get {
|
|
int width, height;
|
|
SDL.SDL_GetWindowMaximumSize(window, out width, out height);
|
|
FloatVector2 maxSize = new FloatVector2();
|
|
maxSize.x = width;
|
|
maxSize.y = height;
|
|
return maxSize;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The minimum size the window can be at any given time.
|
|
/// </summary>
|
|
/// <value>A vector that represents the minimum width and height of the window with the x and y components respectively.</value>
|
|
public FloatVector2 MinimumSize {
|
|
set {
|
|
SDL.SDL_SetWindowMinimumSize(window, (int)value.x, (int)value.y);
|
|
}
|
|
get {
|
|
int width, height;
|
|
SDL.SDL_GetWindowMinimumSize(window, out width, out height);
|
|
FloatVector2 maxSize = new FloatVector2();
|
|
maxSize.x = width;
|
|
maxSize.y = height;
|
|
return maxSize;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The opacity of the window itself.
|
|
/// This is not supported on all targetted platforms, and therefore, can throw an OptionalSDLException for when this is the case.
|
|
/// </summary>
|
|
/// <value>A float with bounds of [0, 1] representing the opacity of the window.</value>
|
|
public float Opacity {
|
|
set {
|
|
int errorCode = SDL.SDL_SetWindowOpacity(window, value);
|
|
if (errorCode < 0) throw new OptionalSDLException();
|
|
}
|
|
get {
|
|
float value;
|
|
int errorCode = SDL.SDL_GetWindowOpacity(window, out value);
|
|
if (errorCode < 0) throw new OptionalSDLException();
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether or not this window is grabbing the users input by making sure the mouse stays within this windows boundaries.
|
|
/// </summary>
|
|
/// <value>True if grabbing.</value>
|
|
public bool GrabbingInput {
|
|
set {
|
|
SDL.SDL_SetWindowGrab(window, value ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE);
|
|
}
|
|
get {
|
|
return SDL.SDL_GetWindowGrab(window) == SDL.SDL_bool.SDL_TRUE ? true : false;
|
|
}
|
|
}
|
|
|
|
internal uint WindowID {
|
|
get {
|
|
uint id = SDL.SDL_GetWindowID(window);
|
|
if (id == 0) throw new FrameworkSDLException();
|
|
return id;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instantiates a window handle.
|
|
/// </summary>
|
|
public WindowContext(string title, int x = -1, int y = -1, int width = 640, int height = 480, bool specialRegions = false, WindowOption options = default(WindowOption)) {
|
|
GameEngine.Logger.Information(String.Format("Starting openGL window with title \"{0}\"", title));
|
|
window = SDL.SDL_CreateWindow(title, x < 0 ? SDL.SDL_WINDOWPOS_CENTERED : x, y < 0 ? SDL.SDL_WINDOWPOS_CENTERED : y, width, height, SDL.SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_MOUSE_FOCUS | (SDL.SDL_WindowFlags) options);
|
|
if (window == null) {
|
|
throw new FrameworkSDLException();
|
|
}
|
|
glContext = SDL.SDL_GL_CreateContext(window);
|
|
if (glContext == null) {
|
|
throw new FrameworkSDLException();
|
|
}
|
|
|
|
clear = Marshal.GetDelegateForFunctionPointer<GLClear>(SDL.SDL_GL_GetProcAddress("glClear"));
|
|
if (clear == null) throw new FrameworkSDLException();
|
|
clearColour = Marshal.GetDelegateForFunctionPointer<GLClearColour>(SDL.SDL_GL_GetProcAddress("glClearColor"));
|
|
if (clearColour == null) throw new FrameworkSDLException();
|
|
viewport = Marshal.GetDelegateForFunctionPointer<GLViewport>(SDL.SDL_GL_GetProcAddress("glViewport"));
|
|
if (viewport == null) throw new FrameworkSDLException();
|
|
|
|
|
|
|
|
if (specialRegions) {
|
|
if (SDL.SDL_SetWindowHitTest(window, SpecialRegionHit, IntPtr.Zero) < 0) {
|
|
throw new OptionalSDLException();
|
|
}
|
|
}
|
|
|
|
WindowContextsManager.RegisterWindow(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes this window the window that is currently being drawn to.
|
|
/// More specifically, it sets the OpenGL Context associated with this window to be the one
|
|
/// that is actively receiving all OpenGL calls.
|
|
///
|
|
/// If the current context is already of this window, nothing happens.
|
|
/// </summary>
|
|
internal void MakeCurrent() {
|
|
if (SDL.SDL_GL_GetCurrentContext() != glContext) {
|
|
SDL.SDL_GL_MakeCurrent(window, glContext);
|
|
}
|
|
}
|
|
|
|
internal void SwapBuffer() {
|
|
SDL.SDL_GL_SwapWindow(window);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to raise the window to the top.
|
|
/// </summary>
|
|
public void RaiseToTop() {
|
|
SDL.SDL_RestoreWindow(window);
|
|
SDL.SDL_RaiseWindow(window);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the index of the display that this window resides within.
|
|
/// </summary>
|
|
/// <returns>An integer representing the display this window resides within.</returns>
|
|
public int GetDisplayIndex() {
|
|
int index = SDL.SDL_GetWindowDisplayIndex(window);
|
|
if (index < 0) throw new FrameworkSDLException();
|
|
return index;
|
|
}
|
|
|
|
private SDL.SDL_HitTestResult SpecialRegionHit(IntPtr window, IntPtr hitPtr, IntPtr data) {
|
|
SDL.SDL_Point SDLPoint = Marshal.PtrToStructure<SDL.SDL_Point>(hitPtr);
|
|
FloatVector2 point = new FloatVector2(SDLPoint.x, SDLPoint.y);
|
|
WindowRegion region = windowRegionHitEvent.Invoke(point);
|
|
return (SDL.SDL_HitTestResult) region;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposes of this window and it's associated handles.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
WindowContextsManager.DeregisterWindow(this);
|
|
SDL.SDL_GL_DeleteContext(glContext);
|
|
SDL.SDL_DestroyWindow(window);
|
|
}
|
|
|
|
internal void OnResize(int width, int height) {
|
|
resizeEvent?.Invoke(width, height);
|
|
}
|
|
|
|
internal void OnFocusLost() {
|
|
focusLostEvent?.Invoke();
|
|
}
|
|
|
|
internal void OnFocusGained() {
|
|
focusGainedEvent?.Invoke();
|
|
}
|
|
|
|
~WindowContext() {
|
|
Dispose();
|
|
}
|
|
}
|
|
} |