//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// file="Form.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
using StgSharp.Math;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace StgSharp.Graphics
{
///
///
///
public abstract unsafe partial class Form
{
private int _frameCount;
private int _frameSpeed;
private short fpsCount;
#region form thread setting
private List frameAsyncTaskList;
#endregion form thread setting
private IntPtr windowID;
internal LinkedList allControl;
internal Vector4 backgroundColor;
internal TimeSpanProvider fpsCountProvider;
internal TimeSpanProvider frameTimeProvider;
internal IglFunc gl;
internal IntPtr graphicContextID;
internal int height;
internal IntPtr monitor;
internal string name;
internal Form sharedForm;
internal int width;
protected Form()
{
frameAsyncTaskList = new List();
fpsCountProvider = new TimeSpanProvider(1.0, TimeSpanProvider.DefaultProvider);
}
public int CurrentFrameCount => frameTimeProvider.CurrentSpan;
public int FrameSpeed => _frameSpeed;
///
/// OpenGL package binded to current form context.
/// A OpenGL context is only binded to current form.
///
public IglFunc GL => gl;
///
/// Height of this form, represent in
///
public int Height
{
get => height;
set => height = value;
}
public bool ShouldClose
{
get => InternalIO.glfwWindowShouldClose(windowID) != 0;
set => InternalIO.glfwSetWindowShouldClose(windowID, value ? 1 : 0);
}
///
/// Width of this form, represent in
///
public Vec2d Size
{
get => new Vec2d(width, height);
set
{
width = (int)value.X;
height = (int)value.Y;
}
}
///
/// Width of this form, represent in
///
public int Width
{
get => width;
set => width = value;
}
public unsafe IntPtr WindowID => windowID;
internal unsafe IntPtr id => windowID;
protected FrameBufferSizeHandler FrameBufferSizeCallback
{
set
{
IntPtr handlerPtr = Marshal.GetFunctionPointerForDelegate(value);
InternalIO.glfwSetFramebufferSizeCallback(windowID, handlerPtr);
}
}
public abstract void Deinit();
///
/// Necessary operation to init this form.
///
public abstract void Init();
///
/// Behaviour of current form when in a frame cycle.
///
public abstract void Main();
///
/// Init an OpenGl context if current form has not been binded to any context. and set the context binded to
/// current form as current OpenGL context.
///
///
/// Something goes wrong and StgSharp fails in creating a new OpenGL context.
///
public unsafe void MakeAsCurrentContext()
{
if (windowID == default)
{
IntPtr windowPtr;
byte[] tittleBytes = Encoding.UTF8.GetBytes(name);
if (sharedForm == null)
{
windowPtr = InternalIO.glfwCreateWindow(width, height, tittleBytes, monitor, IntPtr.Zero);
}
else
{
windowPtr = InternalIO.glfwCreateWindow(width, height, tittleBytes, monitor, sharedForm.windowID);
}
if (((long)windowPtr == -1) || ((long)windowPtr == -2))
{
throw new Exception("Cannot create window instance!");
}
else
{
windowID = windowPtr;
}
graphicContextID = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(GLcontext)));
GLcontext* contextID = (GLcontext*)graphicContextID;
*contextID = new GLcontext();
contextID->glGetString = (delegate*)IntPtr.Zero;
gl = new GLonRender(contextID);
}
InternalIO.glfwMakeContextCurrent(windowID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void PollEvents()
{
InternalIO.glfwPollEvents();
}
///
/// Set all necessary metadata to current form instance
///
public void SetMetadata(int width, int height, string name, IntPtr monitor, Form sharedForm)
{
this.width = width;
this.height = height;
this.name = name;
this.sharedForm = sharedForm;
this.monitor = monitor;
}
///
/// Resize the current form
///
///
/// A (width,height) representing the new size of current form
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetSize(Vec2d size)
{
this.Size = size;
InternalIO.glfwSetWindowSize(this.windowID, (int)size.X, (int)size.Y);
}
///
/// Resize the current form
///
/// New width of the window
/// New height of the window
public void SetSize(int width, int height)
{
this.Width = width;
this.Height = height;
InternalIO.glfwSetWindowSize(this.windowID, width, height);
}
///
/// Set the size limit of current form
///
///
/// Minimun size of current form. In form of a of (width,height)
///
///
/// Maximum size of current form. In form of a of (width,height)
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetSizeLimit(Vec2d minSize, Vec2d maxSize)
{
InternalIO.glfwSetWindowSizeLimits(
this.windowID,
(int)minSize.X,
(int)minSize.Y,
(int)maxSize.X,
(int)maxSize.Y);
}
///
/// Set the size limit of current form
///
/// Minimum width of current form
/// Minimum height of current form
/// Maximum width of curent form
/// Maximum height of current form
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight)
{ InternalIO.glfwSetWindowSizeLimits(this.windowID, minWidth, minHeight, maxWidth, maxHeight); }
#region form main
///
/// Init this form and render it on screen, and respond with operation
///
public unsafe void Show()
{
if (gl == null)
{
InternalIO.InternalWriteLog($"Attempt to show form {name} before init", LogType.Error);
}
else
{
frameTimeProvider = new TimeSpanProvider(1 / 60.0, TimeSpanProvider.DefaultProvider);
MethodInfo[] methods = this.GetType().GetMethods(
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
if (methods.Length != 0)
foreach (MethodInfo m in methods)
{
if (m.GetCustomAttribute(false) != null)
{
if (!m.IsStatic && m.GetParameters().Length == 0 && m.ReturnType == typeof(void) )
{
if (m.Name != "Main")
{
FrameAsyncThread t = new();
}
}
else
{
throw new NotSupportedException("A render activity cannot have parameters, returning value or be static.");
}
}
}
while (!ShouldClose)
{
Main();
SwapBuffers();
PollEvents();
frameTimeProvider.WaitNextSpan();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InternalDeinit(params object[] args) { Deinit(args); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void SwapBuffers()
{
InternalIO.glfwSwapBuffers(windowID);
}
///
/// Get OpenGL library of this form's context.
///
/// A package contains all opengl APIs
protected IglFunc GetGL()
{
if (StgSharp.API == GraphicAPI.GL)
{
return this.GL;
}
else
{
InternalIO.InternalWriteLog(
"Attempt to get OpenGL API package in other graphic envitronment.",
LogType.Error);
throw new InvalidOperationException("Attempt to get OpenGL API package in other graphic envitronment.");
}
}
unsafe ~Form() { Marshal.FreeHGlobal(graphicContextID); }
#endregion form main
}
///
/// Mark that a method in a
/// should be called when rendering.
///
public class RenderActivityAttribute : Attribute
{
}
}