NUGET-Changes:
+ Materials now have a method to draw. That should allow more freedom on how materials can have a effect on the resulting shader.

~ PostProcessEffect.Draw now needs a source ColorAttachment.
~ Added some missing summaries

GIT-/SOLUTION-Changes:
Remade the folder structure, to something more senseable.
This commit is contained in:
Michel Fedde 2021-05-14 21:38:50 +02:00
parent db7f01dca1
commit 89de4258e1
181 changed files with 584 additions and 698 deletions

View file

@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SM.Intergrations")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SM.Intergrations")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4cb351f4-b3f2-4f77-acc2-02f21dbf5ec2")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4CB351F4-B3F2-4F77-ACC2-02F21DBF5EC2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SM.Intergrations</RootNamespace>
<AssemblyName>SM.Intergrations</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="OpenTK, Version=3.3.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\OpenTK.3.3.1\lib\net20\OpenTK.dll</HintPath>
</Reference>
<Reference Include="ShaderToolParser, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\ShaderToolParser.1.0.0-pre3\lib\net450\ShaderToolParser.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShaderTool\STMaterial.cs" />
<Compile Include="ShaderTool\STMaterialShader.cs" />
<Compile Include="ShaderTool\STPostProcessEffect.cs" />
<Compile Include="ShaderTool\STPostProcessShader.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\renderer\SM.Base\SM.Base.csproj">
<Project>{8e733844-4204-43e7-b3dc-3913cddabb0d}</Project>
<Name>SM.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\renderer\SM.OGL\SM.OGL.csproj">
<Project>{f604d684-bc1d-4819-88b5-8b5d03a17be0}</Project>
<Name>SM.OGL</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.IO.Pipes;
using OpenTK;
using OpenTK.Graphics;
using ShaderToolParser.Nodes;
using ShaderToolParser.Nodes.Textures;
using ShaderToolParser.Variables;
using SM.Base.Drawing;
using SM.Base.Textures;
using SM.Base.Window;
namespace SM.Intergrations.ShaderTool
{
public class STMaterial : Material
{
private Vector4 _tintVector = Vector4.One;
public override Color4 Tint
{
get => Color4.FromXyz(_tintVector);
set => _tintVector = Color4.ToXyz(value);
}
public STMaterial(STPDrawNode node)
{
if (node.OGLEffect == null)
throw new Exception("[ERROR AT IMPORTING MATERIAL] DrawNode didn't contain a OpenGL-shader.");
CustomShader = new STMaterialShader(node);
foreach (KeyValuePair<string, STPVariable> pair in node.Variables)
{
if (pair.Value.Type == STPBasisType.Texture)
ShaderArguments[pair.Key] = new Texture(((STPTextureNode) pair.Value.Texture).Bitmap);
}
}
public override void Draw(DrawContext context)
{
ShaderArguments["MVP"] = context.Instances[0].ModelMatrix * context.ModelMatrix * context.View * context.World;
ShaderArguments["MasterTextureMatrix"] = context.Instances[0].TextureMatrix * context.TextureMatrix;
ShaderArguments["HasVColor"] = context.Mesh.Attributes.Has("color");
ShaderArguments["_MATColor"] = _tintVector;
base.Draw(context);
}
}
}

View file

@ -0,0 +1,70 @@

using System;
using System.Collections.Generic;
using OpenTK;
using ShaderToolParser.Nodes;
using ShaderToolParser.Variables;
using SM.Base.Shaders;
using SM.Base.Textures;
using SM.Base.Window;
using SM.OGL.Shaders;
using SM.OGL.Texture;
namespace SM.Intergrations.ShaderTool
{
public class STMaterialShader : MaterialShader
{
private event Action<DrawContext> _uniforms;
public STMaterialShader(STPDrawNode drawNode) : base(new ShaderFileCollection())
{
if (drawNode.OGLEffect == null)
throw new Exception("[ERROR AT IMPORTING SHADER] DrawNode didn't contain a OpenGL-shader.");
STPCompositeNode composeNode = drawNode.OGLEffect;
ShaderFileFiles.Vertex = new[] { new ShaderFile(composeNode.Vertex.ShaderCode) };
ShaderFileFiles.Fragment = new [] {new ShaderFile(composeNode.Fragment.ShaderCode)};
if (composeNode.Geometry != null)
ShaderFileFiles.Geometry = new[] {new ShaderFile(composeNode.Geometry.ShaderCode)};
foreach (KeyValuePair<string, STPVariable> pair in drawNode.Variables)
{
switch (pair.Value.Type)
{
case STPBasisType.Bool:
_uniforms += context => Uniforms[pair.Key].SetUniform1(context.Material.ShaderArguments.Get(pair.Key, false));
break;
case STPBasisType.Float:
_uniforms += context => Uniforms[pair.Key].SetUniform1(context.Material.ShaderArguments.Get(pair.Key, 0.0f));
break;
case STPBasisType.Vector2:
_uniforms += context => Uniforms[pair.Key].SetUniform2(context.Material.ShaderArguments.Get(pair.Key, Vector2.Zero));
break;
case STPBasisType.Vector3:
_uniforms += context => Uniforms[pair.Key].SetUniform3(context.Material.ShaderArguments.Get(pair.Key, Vector3.Zero));
break;
case STPBasisType.Vector4:
_uniforms += context =>
Uniforms[pair.Key].SetUniform4(context.Material.ShaderArguments.Get(pair.Key, Vector4.Zero));
break;
case STPBasisType.Matrix:
_uniforms += context => Uniforms[pair.Key].SetMatrix4(context.Material.ShaderArguments.Get(pair.Key, Matrix4.Identity));
break;
case STPBasisType.Texture:
_uniforms += context => Uniforms[pair.Key].SetTexture(context.Material.ShaderArguments.Get<TextureBase>(pair.Key, null));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
protected override void DrawProcess(DrawContext context)
{
_uniforms.Invoke(context);
DrawObject(context.ForcedType.GetValueOrDefault(context.Mesh.PrimitiveType), context.Mesh);
}
}
}

View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL4;
using ShaderToolParser.Nodes;
using ShaderToolParser.Nodes.Textures;
using ShaderToolParser.Variables;
using SM.Base.Drawing;
using SM.Base.PostProcess;
using SM.Base.Textures;
using SM.Base.Window;
using SM.OGL.Framebuffer;
using SM.OGL.Texture;
namespace SM.Intergrations.ShaderTool
{
public class STPostProcessEffect : PostProcessEffect
{
private STPostProcessShader _shader;
public ShaderArguments Arguments;
public STPostProcessEffect(STPDrawNode postEffectNode)
{
Arguments = Arguments ?? new ShaderArguments();
if (postEffectNode.OGLEffect == null)
throw new Exception("[ERROR AT IMPORTING EFFECT] DrawNode didn't contain a OpenGL-shader.");
_shader = new STPostProcessShader(postEffectNode);
foreach (KeyValuePair<string, STPVariable> pair in postEffectNode.Variables)
{
if (pair.Value.Type == STPBasisType.Texture)
{
if (pair.Value.Texture == null) continue;
Arguments[pair.Key] = new Texture(((STPTextureNode)pair.Value.Texture).Bitmap);
}
}
}
public override void Draw(ColorAttachment source, DrawContext context)
{
Arguments["_Scene"] = (TextureBase)source;
Arguments["_MVP"] = Mvp;
Arguments["_ViewportSize"] = context.Window.WindowSize;
_shader.Draw(Arguments);
}
}
}

View file

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using ShaderToolParser.Nodes;
using ShaderToolParser.Variables;
using SM.Base.Drawing;
using SM.Base.Objects.Static;
using SM.Base.Textures;
using SM.OGL.Shaders;
using SM.OGL.Texture;
namespace SM.Intergrations.ShaderTool
{
public class STPostProcessShader : GenericShader
{
private event Action<ShaderArguments> _uniforms;
public STPostProcessShader(STPDrawNode postProcessNode) : base(new ShaderFileCollection())
{
if (postProcessNode.OGLEffect == null)
throw new Exception("[ERROR AT IMPORTING SHADER] DrawNode didn't contain a OpenGL-shader.");
STPCompositeNode composeNode = postProcessNode.OGLEffect;
ShaderFileFiles.Vertex = new[] { new ShaderFile(composeNode.Vertex.ShaderCode) };
ShaderFileFiles.Fragment = new[] { new ShaderFile(composeNode.Fragment.ShaderCode) };
if (composeNode.Geometry != null)
ShaderFileFiles.Geometry = new[] { new ShaderFile(composeNode.Geometry.ShaderCode) };
foreach (KeyValuePair<string, STPVariable> pair in postProcessNode.Variables)
{
switch (pair.Value.Type)
{
case STPBasisType.Bool:
_uniforms += context => Uniforms[pair.Key].SetUniform1(context.Get(pair.Key, false));
break;
case STPBasisType.Float:
_uniforms += context => Uniforms[pair.Key].SetUniform1(context.Get(pair.Key, 0.0f));
break;
case STPBasisType.Vector2:
_uniforms += context => Uniforms[pair.Key].SetUniform2(context.Get(pair.Key, Vector2.Zero));
break;
case STPBasisType.Vector3:
_uniforms += context => Uniforms[pair.Key].SetUniform3(context.Get(pair.Key, Vector3.Zero));
break;
case STPBasisType.Vector4:
_uniforms += context =>
Uniforms[pair.Key].SetUniform4(context.Get(pair.Key, Vector4.Zero));
break;
case STPBasisType.Matrix:
_uniforms += context => Uniforms[pair.Key].SetMatrix4(context.Get(pair.Key, Matrix4.Identity));
break;
case STPBasisType.Texture:
_uniforms += context => Uniforms[pair.Key].SetTexture(context.Get<TextureBase>(pair.Key, null));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public void Draw(ShaderArguments arguments)
{
Activate();
Plate.Object.Activate();
_uniforms.Invoke(arguments);
GL.DrawArrays(PrimitiveType.Quads, 0, 4);
CleanUp();
}
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="OpenTK" version="3.3.1" targetFramework="net452" />
<package id="ShaderToolParser" version="1.0.0-pre3" targetFramework="net452" />
</packages>

View file

@ -0,0 +1,39 @@
using SharpDX.XInput;
namespace SM.Optionals.Controls
{
public struct GameController
{
public static float GlobalDeadband = 2500;
private Controller _controller;
public float Deadband { get; set; }
public bool IsConnected => _controller.IsConnected;
public UserIndex Index { get; private set; }
public GameController(int id) : this((UserIndex)id)
{}
public GameController(UserIndex index = UserIndex.Any)
{
_controller = new Controller(index);
Index = index;
Deadband = GlobalDeadband;
}
public GameControllerState GetState()
{
if (!IsConnected)
{
return new GameControllerState(true);
}
Gamepad state = _controller.GetState().Gamepad;
return new GameControllerState(state, ref this);
}
}
}

View file

@ -0,0 +1,57 @@
using System;
using OpenTK;
using SharpDX.XInput;
namespace SM.Optionals.Controls
{
public struct GameControllerState
{
public GameControllerStateThumbs Thumbs;
public GameControllerStateTriggers Triggers;
public GameControllerStateDPad DPad;
public GameControllerStateButtons Buttons;
public bool FromConnected { get; }
internal GameControllerState(bool empty)
{
FromConnected = false;
Thumbs = GameControllerStateThumbs.Default;
Triggers = GameControllerStateTriggers.Default;
DPad = GameControllerStateDPad.Default;
Buttons = GameControllerStateButtons.Default;
}
internal GameControllerState(Gamepad state, ref GameController controller)
{
FromConnected = true;
Thumbs = new GameControllerStateThumbs
{
Left = new Vector2(
Math.Abs((float)state.LeftThumbX) < controller.Deadband ? 0 : (float)state.LeftThumbX / short.MaxValue,
Math.Abs((float)state.LeftThumbY) < controller.Deadband ? 0 : (float)state.LeftThumbY / short.MaxValue),
Right = new Vector2(
Math.Abs((float)state.RightThumbX) < controller.Deadband ? 0 : (float)state.RightThumbX / short.MaxValue,
Math.Abs((float)state.RightThumbY) < controller.Deadband ? 0 : (float)state.RightThumbY / short.MaxValue),
PressedLeft = state.Buttons.HasFlag(GamepadButtonFlags.LeftThumb),
PressedRight = state.Buttons.HasFlag(GamepadButtonFlags.RightThumb)
};
Triggers = new GameControllerStateTriggers()
{
Left = (float)state.LeftTrigger / byte.MaxValue,
Right = (float)state.RightTrigger / byte.MaxValue
};
DPad = new GameControllerStateDPad(state.Buttons);
Buttons = new GameControllerStateButtons(state.Buttons);
}
public override string ToString()
{
return !FromConnected ? "[From a disconnected controller]" : $"Thumbs: [{Thumbs}]; Trigger: [{Triggers}]; DPad: [{DPad}]; Buttons: [{Buttons}]";
}
}
}

View file

@ -0,0 +1,45 @@
using SharpDX.XInput;
namespace SM.Optionals.Controls
{
public struct GameControllerStateButtons
{
public static GameControllerStateButtons Default = new GameControllerStateButtons(GamepadButtonFlags.None);
private GamepadButtonFlags _buttonFlags;
public bool X;
public bool Y;
public bool A;
public bool B;
public bool LB;
public bool RB;
public bool LeftThumb;
public bool RightThumb;
public bool this[GamepadButtonFlags flags] => _buttonFlags.HasFlag(flags);
internal GameControllerStateButtons(GamepadButtonFlags flags)
{
_buttonFlags = flags;
X = flags.HasFlag(GamepadButtonFlags.X);
Y = flags.HasFlag(GamepadButtonFlags.Y);
A = flags.HasFlag(GamepadButtonFlags.A);
B = flags.HasFlag(GamepadButtonFlags.B);
LB = flags.HasFlag(GamepadButtonFlags.LeftShoulder);
RB = flags.HasFlag(GamepadButtonFlags.RightShoulder);
LeftThumb = flags.HasFlag(GamepadButtonFlags.LeftThumb);
RightThumb = flags.HasFlag(GamepadButtonFlags.RightThumb);
}
public override string ToString()
{
return $"X: {(X ? "1" : "0")}; Y: {(Y ? "1" : "0")}; A: {(A ? "1" : "0")}; B: {(B ? "1" : "0")}; LB: {(LB ? "1" : "0")}; RB: {(RB ? "1" : "0")}; LT: {(LeftThumb ? "1" : "0")}; RT: {(RightThumb ? "1" : "0")}";
}
}
}

View file

@ -0,0 +1,28 @@
using SharpDX.XInput;
namespace SM.Optionals.Controls
{
public struct GameControllerStateDPad
{
public static GameControllerStateDPad Default = new GameControllerStateDPad(GamepadButtonFlags.None);
public bool Up;
public bool Down;
public bool Left;
public bool Right;
internal GameControllerStateDPad(GamepadButtonFlags flags)
{
Up = flags.HasFlag(GamepadButtonFlags.DPadUp);
Down = flags.HasFlag(GamepadButtonFlags.DPadDown);
Left = flags.HasFlag(GamepadButtonFlags.DPadLeft);
Right = flags.HasFlag(GamepadButtonFlags.DPadRight);
}
public override string ToString()
{
return
$"Up: {(Up ? "1" : "0")}; Down: {(Down ? "1" : "0")}; Left: {(Left ? "1" : "0")}; Right: {(Right ? "1" : "0")};";
}
}
}

View file

@ -0,0 +1,21 @@
using OpenTK;
namespace SM.Optionals.Controls
{
public struct GameControllerStateThumbs
{
public static GameControllerStateThumbs Default = new GameControllerStateThumbs()
{Left = Vector2.Zero, Right = Vector2.Zero};
public Vector2 Left;
public Vector2 Right;
public bool PressedLeft;
public bool PressedRight;
public override string ToString()
{
return $"Left: ({Left.X}; {Left.Y}){(PressedLeft ? " Pressed" : "")}; Right: ({Right.X}; {Right.Y}){(PressedRight ? " Pressed" : "")}";
}
}
}

View file

@ -0,0 +1,14 @@
namespace SM.Optionals.Controls
{
public struct GameControllerStateTriggers
{
public static GameControllerStateTriggers Default = new GameControllerStateTriggers {Left = 0f, Right = 0f};
public float Left;
public float Right;
public override string ToString()
{
return $"Left: {Left}; Right: {Right}";
}
}
}

View file

@ -0,0 +1,29 @@
using System;
namespace SM.Optionals.Controls
{
public class GameKeybind
{
public Func<GameKeybindContext, object> Keyboard;
public Func<GameKeybindContext, object> Controller;
public Func<GameKeybindContext, object> AI;
public Func<GameKeybindContext, object> this[GameKeybindActorType type]
{
get
{
switch (type)
{
case GameKeybindActorType.AI:
return AI;
case GameKeybindActorType.Keyboard:
return Keyboard;
case GameKeybindActorType.Controller:
return Controller;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
}
}
}

View file

@ -0,0 +1,88 @@
using OpenTK.Input;
namespace SM.Optionals.Controls
{
public enum GameKeybindActorType
{
AI,
Keyboard,
Controller
}
public struct GameKeybindActor
{
private GameKeybindActorType _type;
private GameController? _controller;
private GameKeybindHost _keybindHost;
public GameKeybindActorType Type => _type;
public GameController? Controller => _controller;
public object[] Parameter;
private GameKeybindActor(GameKeybindActorType type, GameController? controller)
{
_type = type;
_controller = controller;
_keybindHost = null;
Parameter = new object[0];
}
public void ConnectHost(GameKeybindHost host)
{
_keybindHost = host;
}
public ReturnType Get<ReturnType>(string name, params object[] param)
{
return (ReturnType) Get(name, param);
}
public object Get(string name, params object[] objects)
{
if (_keybindHost == null) return null;
if (!_keybindHost._actions.ContainsKey(name)) return null;
GameKeybind keybind = _keybindHost._actions[name];
GameKeybindContext context = new GameKeybindContext()
{
Actor = this,
Host = _keybindHost,
ActorParameter = Parameter,
InstanceParameter = objects,
KeyboardState = Keyboard.GetState(),
MouseState = Mouse.GetState(),
ControllerState = Controller?.GetState(),
};
return keybind[Type].Invoke(context);
}
public static GameKeybindActor CreateAIActor()
{
return new GameKeybindActor(GameKeybindActorType.AI, null);
}
public static GameKeybindActor CreateKeyboardActor()
{
return new GameKeybindActor(GameKeybindActorType.Keyboard, null);
}
public static GameKeybindActor CreateControllerActor(int id)
{
return CreateControllerActor(new GameController(id));
}
public static GameKeybindActor CreateControllerActor(GameController controller)
{
return new GameKeybindActor(GameKeybindActorType.Controller, controller);
}
}
}

View file

@ -0,0 +1,17 @@
using OpenTK.Input;
namespace SM.Optionals.Controls
{
public struct GameKeybindContext
{
public KeyboardState KeyboardState;
public MouseState MouseState;
public GameControllerState? ControllerState;
public GameKeybindActor Actor;
public GameKeybindHost Host;
public object[] InstanceParameter;
public object[] ActorParameter;
}
}

View file

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
namespace SM.Optionals.Controls
{
public class GameKeybindHost
{
internal Dictionary<string, GameKeybind> _actions = new Dictionary<string, GameKeybind>();
public GameKeybindHost()
{ }
public GameKeybindHost(GameKeybindList setup)
{
for (int i = 0; i < setup.Count; i++)
{
_actions[setup[i].Key] = setup[i].Value;
}
}
public void Setup(string name, Func<GameKeybindContext, object> keyboard = null, Func<GameKeybindContext, object> gameController = null, Func<GameKeybindContext, object> ai = null)
{
GameKeybind bind;
if (_actions.ContainsKey(name))
{
bind = _actions[name];
}
else
{
bind = new GameKeybind();
_actions.Add(name, bind);
}
bind.Keyboard = keyboard;
bind.Controller = gameController;
bind.AI = ai;
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace SM.Optionals.Controls
{
public class GameKeybindList : List<KeyValuePair<string, GameKeybind>>
{
public void Add(string name, GameKeybind keybind)
{
Add(new KeyValuePair<string, GameKeybind>(name, keybind));
}
public void Add(string name, Func<GameKeybindContext, object> keyboard = null,
Func<GameKeybindContext, object> controller = null)
{
Add(new KeyValuePair<string, GameKeybind>(name, new GameKeybind()
{
AI = null,
Controller = controller,
Keyboard = keyboard
}));
}
}
}

View file

@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SM.Game")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SM.Game")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("079bab31-3dc4-40da-90c7-efaa8517c647")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{079BAB31-3DC4-40DA-90C7-EFAA8517C647}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SM.Utils</RootNamespace>
<AssemblyName>SM.Utils</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="OpenTK, Version=3.3.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\OpenTK.3.3.1\lib\net20\OpenTK.dll</HintPath>
</Reference>
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath>
</Reference>
<Reference Include="SharpDX.XInput, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SharpDX.XInput.4.2.0\lib\net45\SharpDX.XInput.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Controls\GameController.cs" />
<Compile Include="Controls\GameControllerState.cs" />
<Compile Include="Controls\GameControllerStateButtons.cs" />
<Compile Include="Controls\GameControllerStateDPad.cs" />
<Compile Include="Controls\GameControllerStateThumbs.cs" />
<Compile Include="Controls\GameControllerStateTriggers.cs" />
<Compile Include="Controls\GameKeybind.cs" />
<Compile Include="Controls\GameKeybindActor.cs" />
<Compile Include="Controls\GameKeybindContext.cs" />
<Compile Include="Controls\GameKeybindHost.cs" />
<Compile Include="Controls\GameKeybindList.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="OpenTK" version="3.3.1" targetFramework="net452" />
<package id="SharpDX" version="4.2.0" targetFramework="net452" />
<package id="SharpDX.XInput" version="4.2.0" targetFramework="net452" />
</packages>

View file

@ -0,0 +1,20 @@
using OpenTK;
namespace SM.Base.Animation
{
/// <summary>
/// Preset Animation curves.
/// </summary>
public class AnimationCurves
{
/// <summary>
/// Linear Curve
/// </summary>
public static readonly BezierCurve Linear = new BezierCurve(Vector2.Zero, Vector2.One);
/// <summary>
/// Smooth curve
/// </summary>
public static readonly BezierCurve Smooth = new BezierCurve(Vector2.Zero, new Vector2(.5f, 0), new Vector2(.5f, 1), Vector2.One);
}
}

View file

@ -0,0 +1,90 @@
using System;
using OpenTK;
using SM.Base.Time;
using SM.Base.Types;
using SM.Base.Window;
namespace SM.Base.Animation
{
/// <summary>
/// A handle to control the interpolation process.
/// </summary>
public class InterpolationProcess : Timer
{
/// <summary>
/// The CVector object, that is interpolated.
/// </summary>
public CVectorBase TargetVector { get; set; }
/// <summary>
/// From where the interpolation process started.
/// </summary>
public Vector4 From { get; }
/// <summary>
/// To where the interpolation is heading.
/// </summary>
public Vector4 To { get; }
/// <summary>
/// The direction towards the <see cref="To"/>
/// </summary>
public Vector4 Direction { get; }
/// <summary>
/// Gets/Sets the interpolation curve.
/// </summary>
public BezierCurve InterpolationCurve { get; set; }
internal InterpolationProcess(CVectorBase targetVector, TimeSpan timeSpan, Vector4 from, Vector4 to, BezierCurve interpolationCurve) : base(timeSpan)
{
TargetVector = targetVector;
From = from;
To = to;
InterpolationCurve = interpolationCurve;
Direction = to - from;
}
/// <summary>
/// Stops the interpolation process.
/// <para>This keeps the state where the interpolation was.</para>
/// </summary>
public new void Stop()
{
Stop(true);
}
/// <summary>
/// Stops the interplation process.
/// </summary>
/// <param name="keepState">If true, it will not set the state to the <see cref="To"/> state.</param>
public void Stop(bool keepState)
{
if (Active && !keepState) SetTarget(To);
base.Stop();
}
private protected override void Ticking(UpdateContext context)
{
base.Ticking(context);
float posInCurve = InterpolationCurve.CalculatePoint(ElapsedNormalized).Y;
Vector4 nextPos = From + (Direction * posInCurve);
SetTarget(nextPos);
}
/// <inheritdoc />
protected override void Stopping(UpdateContext context)
{
base.Stopping(context);
SetTarget(To);
}
private void SetTarget(Vector4 vec)
{
TargetVector.SetRaw(vec.X, vec.Y, vec.Z, vec.W);
}
}
}

View file

@ -0,0 +1,188 @@
#region usings
using System;
using System.Collections.Generic;
using OpenTK.Input;
#endregion
namespace SM.Base.Controls
{
/// <summary>
/// A static class to get keyboard inputs.
/// </summary>
public static class Keyboard
{
private static KeyboardState? _keyboardState;
private static List<Key> _lastPressedKeys = new List<Key>();
/// <summary>
/// True, when ANY key pressed.
/// </summary>
public static bool IsAnyKeyPressed => _keyboardState?.IsAnyKeyDown == true;
internal static void SetStage()
{
if (_keyboardState.HasValue)
{
_lastPressedKeys = new List<Key>();
foreach (object o in Enum.GetValues(typeof(Key)))
if (_keyboardState.Value[(Key) o])
_lastPressedKeys.Add((Key) o);
}
_keyboardState = OpenTK.Input.Keyboard.GetState();
}
/// <summary>
/// Checks if a key is down.
/// </summary>
/// <param name="key">The key</param>
/// <param name="once">If true, the method doesn't return true, when it was pressed one stage before.</param>
/// <returns></returns>
public static bool IsDown(Key key, bool once = false)
{
return _keyboardState?[key] == true && !(once && _lastPressedKeys.Contains(key));
}
/// <summary>
/// Checks if a key was down but not anymore.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool WasDown(Key key)
{
return _keyboardState?[key] == false && _lastPressedKeys.Contains(key);
}
/// <summary>
/// Check if a is up.
/// </summary>
/// <param name="key"></param>
/// <param name="once">If true, the method doesn't return true, when it was up one stage before.</param>
/// <returns></returns>
public static bool IsUp(Key key, bool once = false)
{
return _keyboardState?[key] == false && !(once && !_lastPressedKeys.Contains(key));
}
/// <summary>
/// Checks if specific keys are down.
/// </summary>
/// <param name="startIndex">Startindex</param>
/// <param name="endIndex">Endindex</param>
/// <param name="once">If true, it ignores keys that were down a state before.</param>
/// <returns>True if any of the specific keys where found down.</returns>
/// <exception cref="ArgumentException">The start index can't be greater then the end index.</exception>
public static bool AreSpecificKeysPressed(int startIndex, int endIndex, bool once = false)
{
if (startIndex > endIndex)
throw new ArgumentException("The startIndex is greater than the endIndex.", nameof(startIndex));
int length = endIndex - startIndex;
for (int i = 0; i < length + 1; i++)
{
int actualIndex = i + startIndex;
Key key = (Key) actualIndex;
if (IsDown(key, once)) return true;
}
return false;
}
/// <summary>
/// Checks if any of the specific keys are pressed.
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
public static bool AreSpecificKeysPressed(params Key[] keys)
{
return AreSpecificKeysPressed(false, keys);
}
/// <summary>
/// Checks if any of the specific keys are pressed.
/// </summary>
/// <param name="once">If true, it ignores keys that were down a state before.</param>
/// <param name="keys"></param>
/// <returns></returns>
public static bool AreSpecificKeysPressed(bool once, params Key[] keys)
{
foreach (Key key in keys)
if (IsDown(key, once))
return true;
return false;
}
/// <summary>
/// Checks if specific keys are down and returns the pressed keys.
/// </summary>
/// <param name="startIndex">Startindex</param>
/// <param name="endIndex">Endindex</param>
/// <param name="pressedKeys"></param>
/// <param name="once">If true, it ignores keys that were down a state before.</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static bool AreSpecificKeysPressed(int startIndex, int endIndex, out Key[] pressedKeys,
bool once = false)
{
if (startIndex > endIndex)
throw new ArgumentException("The startIndex is greater than the endIndex.", nameof(startIndex));
int length = endIndex - startIndex;
bool success = false;
List<Key> keys = new List<Key>();
for (int i = 0; i < length + 1; i++)
{
int actualIndex = i + startIndex;
Key key = (Key) actualIndex;
if (IsDown(key, once))
{
keys.Add(key);
success = true;
}
}
pressedKeys = keys.ToArray();
return success;
}
/// <summary>
/// Checks if any of the specific keys are pressed and returns them.
/// </summary>
/// <param name="pressedKey"></param>
/// <param name="keys"></param>
/// <returns></returns>
public static bool AreSpecificKeysPressed(out Key[] pressedKey, params Key[] keys)
{
return AreSpecificKeysPressed(false, out pressedKey, keys);
}
/// <summary>
/// Checks if any of the specific keys are pressed and returns them.
/// </summary>
/// <param name="once">If true, it ignores keys that were down a state before.</param>
/// <param name="pressedKeys"></param>
/// <param name="keys"></param>
/// <returns></returns>
public static bool AreSpecificKeysPressed(bool once, out Key[] pressedKeys, params Key[] keys)
{
List<Key> pressedKey = new List<Key>();
bool success = false;
foreach (Key key in keys)
if (IsDown(key, once))
{
pressedKey.Add(key);
success = true;
}
pressedKeys = pressedKey.ToArray();
return success;
}
}
}

View file

@ -0,0 +1,88 @@
#region usings
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Input;
using SM.Base.Window;
#endregion
namespace SM.Base.Controls
{
/// <summary>
/// Mouse controller
/// </summary>
public class Mouse
{
private static MouseState? _mouseState;
private static List<MouseButton> _lastButtonsPressed = new List<MouseButton>();
/// <summary>
/// The current position of the mouse in the screen.
/// </summary>
public static Vector2 InScreen { get; private set; }
/// <summary>
/// The current position of the mouse in the screen from 0..1.
/// </summary>
public static Vector2 InScreenNormalized { get; private set; }
/// <summary>
/// This returns true, if the left mouse button was pressed.
/// <para>Its pretty much: IsDown(MouseButton.Left, true)</para>
/// </summary>
public static bool LeftClick => IsDown(MouseButton.Left, true);
/// <summary>
/// This returns true, if the right mouse button was pressed.
/// <para>Its pretty much: IsDown(MouseButton.Right, true)</para>
/// </summary>
public static bool RightClick => IsDown(MouseButton.Right, true);
/// <summary>
/// The event to update the values.
/// </summary>
/// <param name="mmea">The event args.</param>
/// <param name="window">The window where the mouse is checked</param>
internal static void MouseMoveEvent(MouseMoveEventArgs mmea, IGenericWindow window)
{
InScreen = new Vector2(mmea.X, mmea.Y);
InScreenNormalized = new Vector2(mmea.X / (float) window.Width, mmea.Y / (float) window.Height);
}
internal static void SetState()
{
if (_mouseState.HasValue)
{
_lastButtonsPressed = new List<MouseButton>();
foreach (object o in Enum.GetValues(typeof(MouseButton)))
if (_mouseState.Value[(MouseButton) o])
_lastButtonsPressed.Add((MouseButton) o);
}
_mouseState = OpenTK.Input.Mouse.GetState();
}
/// <summary>
/// Checks if the mouse is pressed.
/// </summary>
/// <param name="button"></param>
/// <param name="once">If true, it will not get called, when it was pressed in the last update.</param>
public static bool IsDown(MouseButton button, bool once = false)
{
return _mouseState?[button] == true && !(once && _lastButtonsPressed.Contains(button));
}
/// <summary>
/// Checks if the mouse is not pressed.
/// </summary>
/// <param name="button"></param>
/// <param name="once">If true, it will not get called, when it was not pressed in the last update.</param>
public static bool IsUp(MouseButton button, bool once = false)
{
return _mouseState?[button] == false && !(once && !_lastButtonsPressed.Contains(button));
}
}
}

View file

@ -0,0 +1,118 @@
#region usings
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Scene;
using SM.Base.Window;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Contains general basis systems for drawing objects.
/// </summary>
public abstract class DrawingBasis : IShowItem, IModelItem
{
/// <summary>
/// The camera, that was used last time the object was rendered.
/// </summary>
public GenericCamera LastDrawingCamera { get; private set; }
/// <summary>
/// The material it should use.
/// </summary>
public Material Material { get; set; } = new Material();
/// <summary>
/// Transformation for the textures.
/// </summary>
public TextureTransformation TextureTransform { get; set; } = new TextureTransformation();
/// <summary>
/// This allows custom shaders to add own arguments.
/// </summary>
public ShaderArguments ShaderArguments => Material.ShaderArguments;
/// <summary>
/// This can force a shader to render the object with the specified mesh type.
/// </summary>
public PrimitiveType? ForcedMeshType { get; set; }
/// <summary>
/// The mesh it should use.
/// </summary>
public GenericMesh Mesh { get; set; } = SMRenderer.DefaultMesh;
/// <inheritdoc />
public object Parent { get; set; }
/// <inheritdoc />
public string Name { get; set; } = "Unnamed draw object";
/// <inheritdoc />
public ICollection<string> Flags { get; set; }
/// <summary>
/// This value determents if the object should draw something.
/// </summary>
public bool Active { get; set; } = true;
/// <inheritdoc />
public bool RenderActive { get; set; } = true;
/// <inheritdoc />
public void Draw(DrawContext context)
{
context.Material = Material;
context.Mesh = Mesh;
DrawContext(ref context);
}
/// <inheritdoc />
public virtual void OnAdded(object sender)
{
}
/// <inheritdoc />
public virtual void OnRemoved(object sender)
{
}
/// <summary>
/// Draws the context, that was given to them.
/// </summary>
/// <param name="context"></param>
protected virtual void DrawContext(ref DrawContext context)
{
context.ForcedType = ForcedMeshType;
context.TextureMatrix *= TextureTransform.GetMatrix();
context.LastObject = this;
LastDrawingCamera = context.UseCamera;
}
}
/// <summary>
/// Contains general basis systems for drawing objects.
/// </summary>
/// <typeparam name="TTransformation">The transformation type</typeparam>
public abstract class DrawingBasis<TTransformation> : DrawingBasis, IShowTransformItem<TTransformation>
where TTransformation : GenericTransformation, new()
{
/// <summary>
/// The current transformation.
/// </summary>
public TTransformation Transform { get; set; } = new TTransformation();
/// <inheritdoc />
protected override void DrawContext(ref DrawContext context)
{
base.DrawContext(ref context);
Transform.LastMaster = context.ModelMatrix;
context.ModelMatrix = Transform.InWorldSpace;
}
}
}

View file

@ -0,0 +1,72 @@
#region usings
using OpenTK;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Contains methods for using transformations right.
/// </summary>
public abstract class GenericTransformation
{
/// <summary>
/// If true, ignores the transformation and sends <see cref="Matrix4.Identity" />, when requested.
/// </summary>
public bool Ignore = false;
/// <summary>
/// The last matrix that was used to calculate the real world matrix.
/// </summary>
public Matrix4 LastMaster { get; set; }
/// <summary>
/// The transformation in world space.
/// </summary>
public Matrix4 InWorldSpace => MergeMatrix(LastMaster);
/// <summary>
/// Contains the current model matrix.
/// </summary>
protected Matrix4 _modelMatrix { get; private set; }
/// <summary>
/// Contains the last frame the matrix was calculated.
/// </summary>
protected ulong _lastFrame { get; private set; }
/// <summary>
/// Returns the current model matrix.
/// </summary>
/// <returns></returns>
public Matrix4 GetMatrix()
{
if (Ignore) return Matrix4.Identity;
if (_lastFrame != SMRenderer.CurrentFrame)
{
_lastFrame = SMRenderer.CurrentFrame;
_modelMatrix = RequestMatrix();
}
return _modelMatrix;
}
/// <summary>
/// This combines the current matrix with the provided one.
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
public Matrix4 MergeMatrix(Matrix4 matrix)
{
return GetMatrix() * matrix;
}
/// <summary>
/// Calculates the current matrix.
/// </summary>
/// <returns>The current matrix.</returns>
protected abstract Matrix4 RequestMatrix();
}
}

View file

@ -0,0 +1,24 @@
#region usings
using OpenTK;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// This represens a drawing instance.
/// </summary>
public class Instance
{
/// <summary>
/// The model matrix.
/// </summary>
public Matrix4 ModelMatrix = Matrix4.Identity;
/// <summary>
/// The Texture matrix
/// </summary>
public Matrix3 TextureMatrix = Matrix3.Identity;
}
}

View file

@ -0,0 +1,47 @@
#region usings
using OpenTK.Graphics;
using SM.Base.Shaders;
using SM.Base.Window;
using SM.OGL.Texture;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Represents a material.
/// </summary>
public class Material
{
/// <summary>
/// A setting to enable Blending.
/// </summary>
public virtual bool Blending { get; set; } = false;
/// <summary>
/// A custom shader, that is used to draw this material.
/// </summary>
public virtual MaterialShader CustomShader { get; set; }
/// <summary>
/// The base texture. (aka. Diffuse Texture)
/// </summary>
public virtual TextureBase Texture { get; set; }
/// <summary>
/// The tint or color.
/// </summary>
public virtual Color4 Tint { get; set; } = Color4.White;
/// <summary>
/// This allows custom shaders to use own shader arguments.
/// </summary>
public ShaderArguments ShaderArguments { get; internal set; } = new ShaderArguments();
public virtual void Draw(DrawContext context)
{
context.Shader.Draw(context);
}
}
}

View file

@ -0,0 +1,212 @@
#region usings
using System;
using System.Collections.Generic;
using OpenTK;
using SM.Base.Scene;
using SM.Base.Time;
using SM.Base.Window;
using Stopwatch = System.Diagnostics.Stopwatch;
#endregion
namespace SM.Base.Drawing.Particles
{
/// <summary>
/// The (drawing) basis for particles
/// </summary>
public abstract class ParticleDrawingBasis<TTransform, TDirection> : DrawingBasis<TTransform>, IScriptable
where TTransform : GenericTransformation, new()
where TDirection : struct
{
private float? _continuesIntervalSeconds = null;
private Interval _continuesInterval;
/// <summary>
/// The stopwatch of the particles.
/// </summary>
protected Timer timer;
/// <summary>
/// This contains the different instances for the particles.
/// </summary>
protected List<ParticleInstance<TDirection>> instances;
/// <summary>
/// The amount of particles
/// </summary>
public int Amount = 32;
/// <summary>
/// The base lifetime for particles in seconds.
/// </summary>
public float Lifetime;
/// <summary>
/// Randomizes the lifetime for particles.
/// </summary>
public float LifetimeRandomize = 0;
/// <summary>
/// If set to any value (except null), it will create the particles continuously.
/// </summary>
public float? ContinuousInterval
{
get => _continuesIntervalSeconds;
set
{
if (value.HasValue)
{
_continuesInterval.Target = value.Value;
}
_continuesIntervalSeconds = value;
}
}
/// <summary>
/// If true, the particles will spawn in Worldspace and can't be moved by the transformation.
/// </summary>
public bool DetachedParticles;
/// <summary>
/// The maximum speed of the particles
/// <para>Default: 25</para>
/// </summary>
public float MaxSpeed = 25;
/// <summary>
/// Sets up the timer.
/// </summary>
/// <param name="duration">Duration how long the particles should live</param>
protected ParticleDrawingBasis(TimeSpan duration)
{
timer = new Timer(duration);
_continuesInterval = new Interval(0);
_continuesInterval.End += CreateContinuesParticles;
Lifetime = (float) duration.TotalSeconds;
}
/// <summary>
/// Get/Sets the state of pausing.
/// </summary>
public bool Paused
{
get => timer.Paused;
set => timer.Paused = value;
}
/// <summary>
/// Controls the movement of each particles.
/// </summary>
public abstract Func<ParticleInstance<TDirection>, TDirection> MovementCalculation { get; set; }
/// <inheritdoc />
public bool UpdateActive {
get => timer.Active || _continuesInterval.Active;
set { return; }
}
/// <inheritdoc />
public void Update(UpdateContext context)
{
Stopwatch stp = new Stopwatch();
stp.Start();
for (int i = 0; i < instances.Count; i++)
{
instances[i].Lifetime -= context.Deltatime;
if (instances[i].Lifetime <= 0)
{
instances.Remove(instances[i]);
break;
}
instances[i].ModelMatrix = CreateMatrix(instances[i], MovementCalculation(instances[i]));
}
Console.WriteLine();
}
/// <summary>
/// Triggers the particles.
/// </summary>
public void Trigger()
{
instances = new List<ParticleInstance<TDirection>>();
if (_continuesIntervalSeconds.HasValue)
{
_continuesInterval.Target = _continuesIntervalSeconds.Value;
_continuesInterval.Start();
return;
}
timer.Start();
CreateParticles();
}
/// <summary>
/// Stops the particles.
/// </summary>
public void Stop()
{
if (_continuesInterval.Active)
{
_continuesInterval.Stop();
}
timer.Stop();
}
/// <inheritdoc />
public override void OnRemoved(object sender)
{
base.OnRemoved(sender);
Stop();
}
/// <inheritdoc />
protected override void DrawContext(ref DrawContext context)
{
if (!timer.Active && _continuesInterval != null && !_continuesInterval.Active) return;
base.DrawContext(ref context);
if (DetachedParticles) context.ModelMatrix = Matrix4.Identity;
context.Instances = instances.ConvertAll(a => (Instance)a);
Material.Draw(context);
}
/// <summary>
/// Creates the particles.
/// </summary>
protected virtual void CreateParticles()
{
for (int i = 0; i < Amount; i++)
{
instances.Add(CreateObject(i));
}
}
private void CreateContinuesParticles(Timer arg1, UpdateContext arg2)
{
instances.Add(CreateObject(0));
}
/// <summary>
/// Creates a particle.
/// </summary>
protected abstract ParticleInstance<TDirection> CreateObject(int index);
/// <summary>
/// Generates the desired matrix for drawing.
/// </summary>
protected abstract Matrix4 CreateMatrix(ParticleInstance<TDirection> Struct, TDirection relativePosition);
}
}

View file

@ -0,0 +1,40 @@
using OpenTK;
namespace SM.Base.Drawing.Particles
{
/// <summary>
/// This describes a instance of a particle
/// </summary>
public class ParticleInstance : Instance
{
/// <summary>
/// The lifetime the particle started with.
/// </summary>
public float StartLifetime = 0;
/// <summary>
/// The lifetime this particular particle still has.
/// </summary>
public float Lifetime = 0;
/// <summary>
/// A additional matrix to store rotation and scale.
/// </summary>
public Matrix4 Matrix;
/// <summary>
/// Speeeeeeeeeed
/// </summary>
public float Speed;
}
/// <inheritdoc />
public class ParticleInstance<TValue> : ParticleInstance
where TValue : struct
{
/// <summary>
/// A direction, that the particle should travel.
/// </summary>
public TValue Direction;
}
}

View file

@ -0,0 +1,30 @@
#region usings
using OpenTK;
#endregion
namespace SM.Base.Drawing.Particles
{
/// <summary>
/// Contains methods for particle movements.
/// </summary>
public class ParticleMovement
{
/// <summary>
/// Default movement for 2D.
/// </summary>
public static Vector2 Default2D(ParticleInstance<Vector2> particle)
{
return particle.Direction * ((particle.StartLifetime - particle.Lifetime) * particle.Speed);
}
/// <summary>
/// Default movement for 3D.
/// </summary>
public static Vector3 Default3D(ParticleInstance<Vector3> particle)
{
return particle.Direction * ((particle.StartLifetime - particle.Lifetime) * particle.Speed);
}
}
}

View file

@ -0,0 +1,26 @@
#region usings
using System.Collections.Generic;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// A custom dictionary, with a few useful methods.
/// </summary>
public class ShaderArguments : Dictionary<string, object>
{
/// <summary>
/// Returns the stored value or the default value if the name doesn't exist.
/// </summary>
/// <param name="name"></param>
/// <param name="defaultValue"></param>
/// <typeparam name="TType"></typeparam>
/// <returns></returns>
public TType Get<TType>(string name, TType defaultValue = default)
{
return ContainsKey(name) ? (TType) this[name] : defaultValue;
}
}
}

View file

@ -0,0 +1,37 @@
#region usings
using System;
using OpenTK;
#endregion
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Contains information for a font character.
/// </summary>
[Serializable]
public struct CharParameter
{
/// <summary>
/// The advance on the X-axis.
/// </summary>
public int Advance;
/// <summary>
/// The bearing for this char.
/// </summary>
public float BearingX;
/// <summary>
/// The width of the character.
/// </summary>
public float Width;
/// <summary>
/// Matrix for the texture.
/// </summary>
public Matrix3 TextureMatrix;
}
}

View file

@ -0,0 +1,125 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using OpenTK;
using SharpFont;
using SM.Base.Textures;
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Represents a font to be used in DrawText-classes.
/// </summary>
public class Font : Texture
{
private static Library _lib;
private Face _fontFace;
/// <summary>
/// The amount the cursor should move forward when a space was found.
/// </summary>
public float SpaceWidth { get; private set; } = 0;
/// <summary>
/// The char set the font should contain.
/// <para>See <see cref="FontCharStorage"/> for some default values.</para>
/// <para>Default: <see cref="FontCharStorage.SimpleUTF8"/></para>
/// </summary>
public ICollection<char> CharSet { get; set; } = FontCharStorage.SimpleUTF8;
/// <summary>
/// The font-size defines how large the result texture should be.
/// <para>Lower equals less quality, but also less memory usage.</para>
/// <para>Higher equals high quality, but also high memory usage.</para>
/// <para>Default: 12</para>
/// </summary>
public float FontSize { get; set; } = 12;
/// <summary>
/// The character positions.
/// </summary>
public Dictionary<char, CharParameter> Positions = new Dictionary<char, CharParameter>();
/// <summary>
/// Creates a font, by using a path
/// </summary>
/// <param name="path">Path to the font-file.</param>
public Font(string path)
{
_lib ??= new Library();
_fontFace = new Face(_lib, path);
UnpackAlignment = 1;
}
/// <summary>
/// (Re-)Generates the texture.
/// </summary>
public void RegenerateTexture()
{
Width = Height = 0;
Positions = new Dictionary<char, CharParameter>();
_fontFace.SetCharSize(0, FontSize, 0, 96);
var pos = new Dictionary<char, float[]>();
foreach (char c in CharSet)
{
_fontFace.LoadChar(c, LoadFlags.Render, LoadTarget.Normal);
pos.Add(c, new []{(float)_fontFace.Glyph.Bitmap.Width, Width});
Width += (int)_fontFace.Glyph.Advance.X + 2;
Height = Math.Max(_fontFace.Glyph.Bitmap.Rows, Height);
}
_fontFace.LoadChar('_', LoadFlags.Render, LoadTarget.Normal);
SpaceWidth = _fontFace.Glyph.Advance.X.ToSingle();
float bBoxHeight = (Math.Abs(_fontFace.BBox.Bottom) + _fontFace.BBox.Top);
float bBoxTopScale = _fontFace.BBox.Top / bBoxHeight;
float baseline = Height * bBoxTopScale + 1;
Map = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(Map))
{
g.Clear(Color.Transparent);
foreach (var keyvalue in pos)
{
_fontFace.LoadChar(keyvalue.Key, LoadFlags.Render, LoadTarget.Normal);
int y = ((int)baseline - (int)_fontFace.Glyph.Metrics.HorizontalBearingY);
g.DrawImageUnscaled(_fontFace.Glyph.Bitmap.ToGdipBitmap(Color.White), (int)keyvalue.Value[1], y);
Vector2 offset = new Vector2(keyvalue.Value[1] / Width, 0);
Vector2 scale = new Vector2(keyvalue.Value[0] / Width, 1);
Positions.Add(keyvalue.Key, new CharParameter()
{
Advance = (int)_fontFace.Glyph.LinearHorizontalAdvance,
BearingX = _fontFace.Glyph.BitmapLeft,
Width = keyvalue.Value[0],
TextureMatrix = TextureTransformation.CalculateMatrix(offset,
scale, 0),
});
}
}
}
/// <inheritdoc />
public override void Compile()
{
RegenerateTexture();
base.Compile();
}
}
}

View file

@ -0,0 +1,29 @@
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Contains default char sets.
/// </summary>
public class FontCharStorage
{
/// <summary>
/// Contains the english alphabet and the common special character.
/// </summary>
public static readonly char[] SimpleUTF8 =
{
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5',
'6',
'7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~'
};
/// <summary>
/// Contains only numbers.
/// </summary>
public static readonly char[] Numbers =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
}
}

View file

@ -0,0 +1,202 @@
#region usings
using System;
using OpenTK;
using OpenTK.Graphics;
using SM.Base.Objects.Static;
using SM.Base.Window;
#endregion
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Represents the options for <see cref="TextDrawingBasis{TTransform}.Origin"/>
/// </summary>
public enum TextOrigin
{
/// <summary>
/// The position equals (0,0) in the left side of the text.
/// </summary>
Left,
/// <summary>
/// The position equals (0,0) in the center of the text.
/// </summary>
Center,
/// <summary>
/// The position equals (0,0) in the right side of the text.
/// </summary>
Right
}
/// <summary>
/// Defines a basis for text drawing.
/// </summary>
/// <typeparam name="TTransform">Transformation type</typeparam>
public abstract class TextDrawingBasis<TTransform> : DrawingBasis<TTransform>
where TTransform : GenericTransformation, new()
{
/// <summary>
/// The different instances for drawing.
/// </summary>
protected Instance[] _instances;
/// <summary>
/// The text, that is drawn.
/// </summary>
protected string _text;
/// <summary>
/// The width of the text object.
/// </summary>
public float Width;
/// <summary>
/// The height of the text object.
/// </summary>
public float Height;
/// <summary>
/// Allow to change the origin of the text.
/// <para>Default: <see cref="TextOrigin.Left"/></para>
/// </summary>
public TextOrigin Origin = TextOrigin.Left;
/// <summary>
/// The spacing between numbers.
/// <para>Default: 1</para>
/// </summary>
public float Spacing = 1f;
/// <summary>
/// The font.
/// </summary>
public Font Font
{
get => (Font)Material.Texture;
set
{
Material.Texture = value;
GenerateMatrixes();
}
}
/// <summary>
/// The text, that is drawn.
/// </summary>
public string Text
{
get => _text;
set
{
_text = value;
GenerateMatrixes();
}
}
/// <summary>
/// The font color.
/// </summary>
public Color4 Color
{
get => Material.Tint;
set => Material.Tint = value;
}
/// <summary>
/// Creates a text object with a font.
/// </summary>
/// <param name="font">The font.</param>
protected TextDrawingBasis(Font font)
{
Material.Texture = font;
Material.Blending = true;
Mesh = Plate.Object;
}
/// <inheritdoc />
protected override void DrawContext(ref DrawContext context)
{
base.DrawContext(ref context);
if (_instances == null) GenerateMatrixes();
}
/// <summary>
/// This generates the instances.
/// </summary>
/// <exception cref="Exception">The font doesn't contain a character that is needed for the text.</exception>
public void GenerateMatrixes()
{
if (!Font.WasCompiled) Font.RegenerateTexture();
_text = _text.Replace("\r\n", "\n").Replace("\t", " ");
_instances = new Instance[_text.Length];
float x = 0;
float y = 0;
for (var i = 0; i < _text.Length; i++)
{
if (_text[i] == ' ')
{
x += Font.SpaceWidth * Spacing;
continue;
}
if (_text[i] == '\n')
{
y += Font.Height;
Width = Math.Max(Width, x);
x = 0;
continue;
}
CharParameter parameter;
try
{
parameter = Font.Positions[_text[i]];
}
catch
{
throw new Exception("Font doesn't contain '" + _text[i] + "'");
}
if (i == 0)
{
x += parameter.Width / 2;
}
var matrix = Matrix4.CreateScale(parameter.Width, Font.Height, 1) *
Matrix4.CreateTranslation(x + parameter.Width / 2, -y, 0);
_instances[i] = new Instance
{
ModelMatrix = matrix,
TextureMatrix = parameter.TextureMatrix
};
x += parameter.Advance;
}
Height = y + Font.Height;
Width = x;
if (Origin != TextOrigin.Left)
{
foreach (Instance i in _instances)
{
if (i == null) continue;
switch (Origin)
{
case TextOrigin.Center:
i.ModelMatrix *= Matrix4.CreateTranslation(-Width / 2, 0, 0);
break;
case TextOrigin.Right:
i.ModelMatrix *= Matrix4.CreateTranslation(-Width, 0, 0);
break;
}
}
}
}
}
}

View file

@ -0,0 +1,89 @@
#region usings
using OpenTK;
using OpenTK.Graphics.ES10;
using SM.Base.Textures;
using SM.Base.Types;
#endregion
namespace SM.Base.Drawing
{
/// <summary>
/// Stores transformations for the textures.
/// </summary>
public class TextureTransformation
{
/// <summary>
/// The offset from the origin.
/// </summary>
public CVector2 Offset = new CVector2(0);
/// <summary>
/// The rotation of the texture.
/// </summary>
public CVector1 Rotation = new CVector1(0);
/// <summary>
/// The scale of the texture.
/// </summary>
public CVector2 Scale = new CVector2(1);
/// <summary>
/// Get the texture matrix.
/// </summary>
/// <returns></returns>
public Matrix3 GetMatrix()
{
return CalculateMatrix(Offset, Scale, Rotation);
}
/// <summary>
/// Sets the offset relative to the pixels of the texture.
/// </summary>
/// <param name="texture">The texture it should use.</param>
/// <param name="pixelLocation">The offset in pixel.</param>
public void SetOffsetRelative(Texture texture, Vector2 pixelLocation)
{
Vector2 textureSize = new Vector2(texture.Width, texture.Height);
Offset.Set( Vector2.Divide(pixelLocation, textureSize) );
}
/// <summary>
/// Sets the scale relative to the pixels of the texture.
/// </summary>
/// <param name="texture">The texture.</param>
/// <param name="rectangleSize">The scale in pixel.</param>
public void SetScaleRelative(Texture texture, Vector2 rectangleSize)
{
Vector2 textureSize = new Vector2(texture.Width, texture.Height);
Scale.Set( Vector2.Divide(rectangleSize, textureSize) );
}
/// <summary>
/// Sets the offset and scale relative to the pixels of the texture.
/// </summary>
/// <param name="texture">The texture.</param>
/// <param name="location">Offset in pixel</param>
/// <param name="rectangleSize">Scale in pixel.</param>
public void SetRectangleRelative(Texture texture, Vector2 location, Vector2 rectangleSize)
{
Vector2 textureSize = new Vector2(texture.Width, texture.Height);
Offset.Set(Vector2.Divide(location, textureSize));
Scale.Set(Vector2.Divide(rectangleSize, textureSize));
}
/// <summary>
/// Calculates a texture matrix.
/// </summary>
/// <param name="offset"></param>
/// <param name="scale"></param>
/// <param name="rotation"></param>
/// <returns></returns>
public static Matrix3 CalculateMatrix(Vector2 offset, Vector2 scale, float rotation)
{
float radians = MathHelper.DegreesToRadians(rotation);
Matrix3 result = Matrix3.CreateScale(scale.X, scale.Y, 1) * Matrix3.CreateRotationZ(radians);
result.Row2 = new Vector3(offset.X, offset.Y, 1);
return result;
}
}
}

View file

@ -0,0 +1,144 @@
#region usings
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Textures;
#endregion
namespace SM.Base.Drawing.Text
{
/// <summary>
/// Represents a font.
/// </summary>
public class Font : Texture
{
/// <summary>
/// The char set for the font.
/// <para>Default: <see cref="FontCharStorage.SimpleUTF8" /></para>
/// </summary>
public ICollection<char> CharSet = FontCharStorage.SimpleUTF8;
/// <summary>
/// The font family, that is used to find the right font.
/// </summary>
public FontFamily FontFamily;
/// <summary>
/// The font size.
/// <para>Default: 12</para>
/// </summary>
public float FontSize = 12;
public float SpaceWidth { get; private set; }
public float Spacing = 1;
/// <summary>
/// The font style.
/// <para>Default: <see cref="System.Drawing.FontStyle.Regular" /></para>
/// </summary>
public FontStyle FontStyle = FontStyle.Regular;
/// <summary>
/// This contains all information for the different font character.
/// </summary>
public Dictionary<char, CharParameter> Positions = new Dictionary<char, CharParameter>();
/// <summary>
/// Generates a font from a font family from the specified path.
/// </summary>
/// <param name="path">The specified path</param>
public Font(string path)
{
var pfc = new PrivateFontCollection();
pfc.AddFontFile(path);
FontFamily = pfc.Families[0];
}
/// <summary>
/// Generates a font from a specified font family.
/// </summary>
/// <param name="font">Font-Family</param>
public Font(FontFamily font)
{
FontFamily = font;
}
/// <inheritdoc />
public override TextureWrapMode WrapMode { get; set; } = TextureWrapMode.ClampToEdge;
/// <summary>
/// Regenerates the texture.
/// </summary>
public void RegenerateTexture()
{
Width = 0;
Height = 0;
Positions = new Dictionary<char, CharParameter>();
var map = new Bitmap(1000, 20);
var charParams = new Dictionary<char, float[]>();
using (var f = new System.Drawing.Font(FontFamily, FontSize, FontStyle))
{
using (var g = Graphics.FromImage(map))
{
g.Clear(Color.Transparent);
foreach (var c in CharSet)
{
var s = c.ToString();
var size = g.MeasureString(s, f, 0, StringFormat.GenericTypographic);
try
{
charParams.Add(c, new[] {size.Width, Width});
}
catch
{
// ignored
}
if (Height < size.Height) Height = (int) size.Height;
Width += (int) size.Width + 1;
}
SpaceWidth = g.MeasureString("_", f, 0, StringFormat.GenericTypographic).Width;
}
map = new Bitmap(Width, Height);
using (var g = Graphics.FromImage(map))
{
foreach (var keyValuePair in charParams)
{
var normalizedX = (keyValuePair.Value[1]+ 0.00001f) / Width;
var normalizedWidth = keyValuePair.Value[0] / Width;
CharParameter parameter;
Positions.Add(keyValuePair.Key, parameter = new CharParameter
{
NormalizedWidth = normalizedWidth,
NormalizedX = normalizedX,
Width = keyValuePair.Value[0],
X = (int) keyValuePair.Value[1]
});
g.DrawString(keyValuePair.Key.ToString(), f, Brushes.White, parameter.X, 0, StringFormat.GenericTypographic);
}
}
}
Map = map;
Recompile();
}
/// <inheritdoc />
public override void Compile()
{
RegenerateTexture();
base.Compile();
}
}
}

248
src/renderer/SM.Base/Log.cs Normal file
View file

@ -0,0 +1,248 @@
#region usings
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL4;
using SM.OGL;
#endregion
namespace SM.Base
{
/// <summary>
/// Specifies the target.
/// </summary>
[Flags]
public enum LogTarget
{
/// <summary>
/// No target, will not draw.
/// </summary>
None = 0,
/// <summary>
/// Takes the <see cref="Log.DefaultTarget" />.
/// </summary>
Default = 1,
/// <summary>
/// Writes the log to the console.
/// </summary>
Console = 2,
/// <summary>
/// Writes the log to the debugger at <see cref="Debug" />.
/// </summary>
Debugger = 4,
/// <summary>
/// Writes the log to the specific file.
/// </summary>
File = 8,
/// <summary>
/// Writes the log to every target.
/// </summary>
All = Console | Debugger | File
}
/// <summary>
/// Preset log types.
/// </summary>
public enum LogType
{
/// <summary>
/// Informations. Console Color: Green
/// </summary>
Info,
/// <summary>
/// Warnings. Console Color: Yellow
/// </summary>
Warning,
/// <summary>
/// Error. Console Color: Red
/// </summary>
Error
}
/// <summary>
/// Contains the system for logging.
/// </summary>
public class Log
{
private static StreamWriter _logStream;
private static bool _init;
/// <summary>
/// Presets for the log targets.
/// </summary>
public static Dictionary<LogTarget, string> Preset = new()
{
{LogTarget.Console, "[%type%] %msg%"},
{LogTarget.Debugger, "[%type%] %msg%"},
{LogTarget.File, "<%date%, %time%> [%type%] %msg%"}
};
private static readonly Dictionary<LogType, ConsoleColor> Colors = new()
{
{LogType.Info, ConsoleColor.Green},
{LogType.Warning, ConsoleColor.Yellow},
{LogType.Error, ConsoleColor.Red}
};
/// <summary>
/// Specified the default target.
/// </summary>
public static LogTarget DefaultTarget = LogTarget.All;
/// <summary>
/// Sets the log file. At wish compresses the old file to a zip file.
/// </summary>
/// <param name="path">The path to the log file.</param>
/// <param name="compressionFolder">Path for the compression, if desired.</param>
public static void SetLogFile(string path = "sm.log", string compressionFolder = "")
{
_logStream?.Close();
if (!_init) Init();
if (File.Exists(path))
{
if (compressionFolder != "")
{
if (!Directory.Exists(compressionFolder)) Directory.CreateDirectory(compressionFolder);
var creation = File.GetLastWriteTime(path);
try
{
using var archive =
ZipFile.Open(
$"{compressionFolder}{Path.DirectorySeparatorChar}{Path.GetFileName(path)}_{creation.Year.ToString() + creation.Month + creation.Day}_{creation.Hour.ToString() + creation.Minute + creation.Second + creation.Millisecond}.zip",
ZipArchiveMode.Create);
archive.CreateEntryFromFile(path, Path.GetFileName(path));
}
catch
{
// ignore
}
}
File.Delete(path);
}
_logStream = new StreamWriter(path) {AutoFlush = true};
Write(LogType.Info, $"Activate new log file. ['{path}']");
}
internal static void Init()
{
if (_init) return;
AppDomain.CurrentDomain.UnhandledException += ExceptionHandler;
AppDomain.CurrentDomain.DomainUnload += (sender, args) =>
{
_logStream.WriteLine("Unload application");
_logStream.Close();
};
GLCustomActions.AtKHRDebug = GLDebugAction;
GLCustomActions.AtError = err => Write(LogType.Error, err);
GLCustomActions.AtWarning = warning => Write(LogType.Warning, warning);
GLCustomActions.AtInfo = info => Write(LogType.Info, info);
_init = true;
}
private static void GLDebugAction(DebugSource source, DebugType type, DebugSeverity severity, string msg)
{
if (type.HasFlag(DebugType.DebugTypeError)) throw new Exception("[GLError] " + msg);
Write("GL"+ (type != DebugType.DontCare ? type.ToString().Substring(9) : "DontCare"), ConsoleColor.Gray, msg);
}
[DebuggerStepThrough]
private static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
Write(e.IsTerminating ? "Terminating Error" : LogType.Error.ToString(),
e.IsTerminating ? ConsoleColor.DarkRed : ConsoleColor.Red, e.ExceptionObject);
if (e.IsTerminating)
{
MessageBox.Show($"Critical error occured.\n\n{e.ExceptionObject}",
$"Terminating Error: {e.ExceptionObject.GetType().Name}");
_logStream?.Close();
}
}
/// <summary>
/// Writes multiple lines of the same type to the log.
/// </summary>
public static void Write<T>(LogType type, params T[] values)
{
Write(type.ToString(), Colors[type], values);
}
/// <summary>
/// Writes multiple lines of the same type to the log.
/// </summary>
public static void Write<T>(string type, ConsoleColor color, params T[] values)
{
for (var i = 0; i < values.Length; i++) Write(type, color, values[i], DefaultTarget);
}
/// <summary>
/// Writes one line to the log.
/// </summary>
public static void Write<T>(LogType type, T value, LogTarget target = LogTarget.Default)
{
Write(type.ToString(), Colors[type], value, target);
}
/// <summary>
/// Writes one line to the log.
/// </summary>
public static void Write<T>(string type, ConsoleColor color, T value, LogTarget target = LogTarget.Default)
{
if (target == LogTarget.Default)
target = DefaultTarget;
if (target.HasFlag(LogTarget.Console))
ColorfulWriteLine(color, ProcessPreset(LogTarget.Console, type, value.ToString()));
if (target.HasFlag(LogTarget.Debugger))
Debug.WriteLine(ProcessPreset(LogTarget.Debugger, type, value.ToString()));
if (target.HasFlag(LogTarget.File))
_logStream?.WriteLine(ProcessPreset(LogTarget.File, type, value.ToString()));
}
/// <summary>
/// Writes a text with a different color.
/// </summary>
/// <param name="color"></param>
/// <param name="value"></param>
public static void ColorfulWriteLine(ConsoleColor color, string value)
{
var before = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(value);
Console.ForegroundColor = before;
}
private static string ProcessPreset(LogTarget target, string type, string msg)
{
var preset = Preset[target];
var now = DateTime.Now;
return preset.Replace("%date%", now.ToShortDateString())
.Replace("%time%", now.ToShortTimeString())
.Replace("%type%", type)
.Replace("%msg%", msg);
}
}
}

View file

@ -0,0 +1,43 @@
#region usings
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Objects
{
/// <summary>
/// This class allows for fast mesh creation.
/// </summary>
public class InstancedMesh : Mesh
{
/// <summary>
/// Generates a mesh, with a primitive and attributes, that are required.
/// </summary>
/// <param name="type">The mesh type</param>
/// <param name="enabledAttibute">A list of (additional) attributes.
/// <para> Possible values: uv, normals and color </para>
/// </param>
public InstancedMesh(PrimitiveType type, string[] enabledAttibute) : base(type)
{
Attributes["vertex"] = Vertex = new VBO<Vector3>();
foreach (string attribute in enabledAttibute)
switch (attribute)
{
case "uv":
Attributes["uv"] = UVs = new VBO<Vector2>();
break;
case "normals":
Attributes["normal"] = Normals = new VBO<Vector3>();
break;
case "color":
Attributes["color"] = Color = new VBO<Color4>();
break;
}
}
}
}

View file

@ -0,0 +1,31 @@
#region usings
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Objects
{
/// <inheritdoc cref="GenericMesh" />
public class Mesh : GenericMesh, ILineMesh
{
/// <summary>
/// While initializing, it will add the <see cref="Color" /> to the data index.
/// </summary>
public Mesh(PrimitiveType type)
{
PrimitiveType = type;
Attributes.Add(3, "color", Color);
}
/// <summary>
/// Contains vertex colors
/// </summary>
public virtual VBO<Color4> Color { get; protected set; }
/// <inheritdoc />
public float LineWidth { get; set; } = 1;
}
}

View file

@ -0,0 +1,52 @@
#region usings
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Objects.Static
{
/// <summary>
/// An AxisHelper-Model
/// <para>White: -X, -Y, -Z</para>
/// <para>Red: +X </para>
/// <para>Green: +Y </para>
/// <para>Blue: +Z </para>
/// </summary>
public class AxisHelper : Mesh
{
/// <summary>
/// Object
/// </summary>
public static AxisHelper Object = new AxisHelper();
private AxisHelper() : base(PrimitiveType.Lines)
{
}
/// <inheritdoc />
public override VBO<Vector3> Vertex { get; protected set; } = new VBO<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(.5f, 0, 0),
new Vector3(0, 0, 0),
new Vector3(0, .5f, 0),
new Vector3(0, 0, -.5f),
new Vector3(0, 0, .5f)
};
/// <inheritdoc />
public override VBO<Color4> Color { get; protected set; } = new VBO<Color4>
{
Color4.White,
Color4.Red,
Color4.White,
Color4.Green,
Color4.White,
Color4.DarkBlue
};
}
}

View file

@ -0,0 +1,52 @@
#region usings
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Objects.Static
{
/// <summary>
/// A basic plate
/// </summary>
public class Plate : GenericMesh
{
/// <summary>
/// The object.
/// </summary>
public static Plate Object = new Plate();
//public override int[] Indices { get; set; } = new[] {0, 1, 2, 3};
private Plate()
{
}
/// <inheritdoc />
public override VBO<Vector3> Vertex { get; protected set; } = new VBO<Vector3>
{
new Vector3(-.5f, -.5f, 0),
new Vector3(-.5f, .5f, 0),
new Vector3(.5f, .5f, 0),
new Vector3(.5f, -.5f, 0),
};
/// <inheritdoc />
public override VBO<Vector2> UVs { get; protected set; } = new VBO<Vector2>
{
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(1, 1),
};
/// <inheritdoc />
public override PrimitiveType PrimitiveType { get; protected set; } = PrimitiveType.Quads;
/// <inheritdoc />
public override BoundingBox BoundingBox { get; } =
new BoundingBox(new Vector3(-.5f, -.5f, 0), new Vector3(.5f, .5f, 0));
}
}

View file

@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View file

@ -0,0 +1,212 @@
#region usings
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Drawing;
using SM.Base.PostProcess;
using SM.Base.Utility;
using SM.Base.Window;
using SM.OGL.Framebuffer;
using SM.OGL.Texture;
#endregion
namespace SM.Base.PostEffects
{
/// <summary>
/// A bloom post process effect.
/// </summary>
public class BloomEffect : PostProcessEffect
{
private static BezierCurve _defaultCurve = new BezierCurve(Vector2.UnitY, Vector2.Zero, new Vector2(0.4f, 0), new Vector2(.5f,0));
private static readonly PostProcessShader _mergeShader = new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom_merge_vert.glsl"),
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom_merge.glsl"));
private static readonly PostProcessShader _shader =
new PostProcessShader(AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom_blur.glsl"));
private const float _defaultTextureScale = .75f;
private Framebuffer _source;
private Framebuffer _bloomBuffer1;
private Framebuffer _bloomBuffer2;
private readonly bool _hdr;
private readonly float _textureScale = .75f;
private BezierCurve _weightCurve;
private float[] _weights;
private ColorAttachment _xBuffer;
private ColorAttachment _yBuffer;
/// <summary>
/// A texture where you can define the amount of the bloom effect at each pixel.
/// </summary>
public TextureBase AmountMap;
/// <summary>
/// The transformation for the amount map.
/// </summary>
public TextureTransformation AmountTransform = new TextureTransformation();
/// <summary>
/// The maximal amount the amount map is clamped to.
/// <para>Default: 1</para>
/// </summary>
public float MaxAmount = 1;
/// <summary>
/// The minimal amount the amount map is clamped to.
/// <para>Default: 0</para>
/// </summary>
public float MinAmount = 0;
/// <summary>
/// The defines how often the x-y-flipflop happens.
/// <para>Default: 8</para>
/// </summary>
public int Iterations = 8;
/// <summary>
/// The Threshold for the bloom effect.
/// <para>Default: .8f</para>
/// </summary>
public float Threshold = .8f;
/// <summary>
/// Increases the brightness of the resulting effect.
/// <para>Default: 1</para>
/// </summary>
public float Power = 1;
/// <summary>
/// Radius of the effect
/// <para>Default: 2</para>
/// </summary>
public float Radius = 1;
/// <summary>
/// This can disable the bloom calculation.
/// <para>Default: true</para>
/// </summary>
public bool Enable = true;
/// <summary>
/// This defines the weight curve.
/// </summary>
public BezierCurve WeightCurve
{
get => _weightCurve;
set
{
_weightCurve = value;
UpdateWeights();
}
}
/// <summary>
/// This defines how many picks the effect should pick from the weight curve.
/// </summary>
public int WeightCurvePickAmount = 4;
/// <summary>
/// This creates a bloom effect.
/// </summary>
/// <param name="source">This can specify a own source framebuffer. If not set, it will take the Pipeline MainFramebuffer.</param>
/// <param name="hdr">This allows to enable hdr returns.</param>
/// <param name="textureScale">This allows for a increase in performance, by lowering the calculating texture scale.</param>
public BloomEffect(Framebuffer source = null, bool hdr = false, float? textureScale = null)
{
_source = source;
_hdr = hdr;
_textureScale = textureScale.GetValueOrDefault(_defaultTextureScale);
WeightCurve = _defaultCurve;
}
private void UpdateWeights()
{
_weights = new float[WeightCurvePickAmount];
for (int i = 0; i < WeightCurvePickAmount; i++)
_weights[i] = _weightCurve.CalculatePoint((float) (i + 1) / (WeightCurvePickAmount + 1)).Y;
}
/// <inheritdoc/>
protected override void InitProcess()
{
_source ??= Pipeline.MainFramebuffer;
_source.ColorAttachments["color"].PixelInformation = PixelInformation.RGBA_HDR;
_bloomBuffer1 = new Framebuffer(Pipeline.ConnectedWindow, _textureScale)
{
Name = "BloomX"
};
_bloomBuffer1.Append("xBuffer", _xBuffer = new ColorAttachment(0, PixelInformation.RGBA_HDR));
_bloomBuffer1.Compile();
_bloomBuffer2 = new Framebuffer(Pipeline.ConnectedWindow, _textureScale)
{
Name = "BloomY"
};
_bloomBuffer2.Append("yBuffer", _yBuffer = new ColorAttachment(0, PixelInformation.RGBA_HDR));
_bloomBuffer2.Compile();
Pipeline.Framebuffers.Add(_bloomBuffer1);
Pipeline.Framebuffers.Add(_bloomBuffer2);
}
/// <inheritdoc/>
public override void Draw(ColorAttachment source, DrawContext context)
{
if (Enable)
{
GL.Viewport(0, 0, (int) (Pipeline.ConnectedWindow.Width * _textureScale),
(int) (Pipeline.ConnectedWindow.Height * _textureScale));
Framebuffer target = Framebuffer.GetCurrentlyActive();
bool first = true, hoz = true;
int iter = Iterations * 2;
for (int i = 0; i < iter; i++)
{
(hoz ? _bloomBuffer1 : _bloomBuffer2).Activate(false);
_shader.Draw(collection =>
{
collection["renderedTexture"].SetTexture(first ? source : (hoz ? _yBuffer : _xBuffer));
collection["First"].SetUniform1(first);
collection["Threshold"].SetUniform1(Threshold);
collection["Horizontal"].SetUniform1(hoz);
collection["Weights"].SetUniform1(_weights);
collection["WeightCount"].SetUniform1(WeightCurvePickAmount);
collection["Power"].SetUniform1(Power);
collection["Radius"].SetUniform1(_textureScale * Radius);
});
hoz = !hoz;
if (first) first = false;
}
GL.Viewport(Pipeline.ConnectedWindow.ClientRectangle);
target.Activate();
}
_mergeShader.Draw(collection =>
{
collection["Scene"].SetTexture(source);
collection["Bloom"].SetTexture(_yBuffer);
collection["MinAmount"].SetUniform1(MinAmount);
collection["MaxAmount"].SetUniform1(MaxAmount);
collection["AmountMap"].SetTexture(AmountMap, collection["HasAmountMap"]);
collection["TextureTransform"].SetMatrix3(AmountTransform.GetMatrix());
collection["Exposure"].SetUniform1(context.UseCamera.Exposure);
collection["HDR"].SetUniform1(_hdr);
});
}
}
}

View file

@ -0,0 +1,74 @@
#region usings
using OpenTK.Graphics.OpenGL4;
using SM.Base.PostProcess;
using SM.Base.Utility;
using SM.OGL.Framebuffer;
#endregion
namespace SM.Base.PostEffects
{
/// <summary>
/// This class has some utility for render pipelines
/// </summary>
public static class PostProcessUtility
{
private static readonly PostProcessShader _hdrExposureShader =
new PostProcessShader(AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".finalize_hdr.glsl"));
private static readonly PostProcessShader _gammaShader =
new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".finalize_gamma.glsl"));
/// <summary>
/// The gamma that is used for <see cref="FinalizeGamma"/> and <see cref="FinalizeHDR"/>.
/// </summary>
public static float Gamma = 2.2f;
/// <summary>
/// This resolves a multisampled framebuffer to a non-multisampled renderbuffer.
/// <para>This removes the depth buffer.</para>
/// </summary>
/// <param name="multisampledBuffers"></param>
/// <param name="target"></param>
public static void ResolveMultisampledBuffers(Framebuffer multisampledBuffers, Framebuffer target)
{
multisampledBuffers.Activate(FramebufferTarget.ReadFramebuffer);
target.Activate(FramebufferTarget.DrawFramebuffer);
GL.BlitFramebuffer(0, 0, (int) multisampledBuffers.Size.X, (int) multisampledBuffers.Size.Y, 0, 0,
(int) target.Size.X, (int) target.Size.Y, ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Nearest);
target.Activate();
}
/// <summary>
/// This converts HDR to LDR and applys gamma.
/// </summary>
/// <param name="attachment"></param>
/// <param name="exposure"></param>
public static void FinalizeHDR(ColorAttachment attachment, float exposure)
{
_hdrExposureShader.Draw(u =>
{
u["Gamma"].SetUniform1(Gamma);
u["Exposure"].SetUniform1(exposure);
u["Scene"].SetTexture(attachment);
});
}
/// <summary>
/// This applys gamma
/// </summary>
/// <param name="attachment"></param>
public static void FinalizeGamma(ColorAttachment attachment)
{
_gammaShader.Draw(u =>
{
u["Gamma"].SetUniform1(Gamma);
u["Scene"].SetTexture(attachment);
});
}
}
}

View file

@ -0,0 +1,48 @@
#version 330
#define PI 3.14159265359
uniform sampler2D renderedTexture;
uniform float RenderScale;
uniform bool First;
uniform float Threshold;
uniform bool Horizontal;
uniform float[32] Weights;
uniform int WeightCount;
uniform float Power;
uniform float Radius;
layout(location = 0) out vec4 color;
vec4 GetRenderColorOffset(vec2 offset);
float brightness(vec3 c)
{
return max(max(c.r, c.g), c.b);
}
float bright;
float GetWeight(int dif) {
return Weights[dif];
}
void main() {
vec3 thres = vec3(First ? Threshold : 0);
vec2 tex_offset = 1.0 / textureSize(renderedTexture, 0) * vec2(Horizontal ? 1 : 0, Horizontal ? 0 : 1);
vec3 result = max(GetRenderColorOffset(vec2(0)).rgb - thres, 0) * (First ? Power : 1) * GetWeight(0);
float radi = Radius + (length(result));
for(int i = 1; i < WeightCount; i++) {
result += max(GetRenderColorOffset(tex_offset * i * radi).rgb - thres, 0) * (First ? Power : 1) * GetWeight(i);
result += max(GetRenderColorOffset(-tex_offset * i * radi).rgb - thres, 0) * (First ? Power : 1) * GetWeight(i);
}
color = vec4(result, 1);
}

View file

@ -0,0 +1,30 @@
#version 330
in vec2 vTexture;
in vec2 TransformedTexture;
uniform sampler2D Scene;
uniform sampler2D Bloom;
uniform float MinAmount;
uniform float MaxAmount;
uniform sampler2D AmountMap;
uniform bool HasAmountMap;
uniform float Exposure;
uniform bool HDR;
layout(location = 0) out vec4 color;
void main() {
vec3 result = texture(Bloom, vTexture).rgb;
if (HasAmountMap) result *= clamp(length(texture(AmountMap, TransformedTexture).rgb) * (MaxAmount - MinAmount) + MinAmount, 0, 1);
if (!HDR) {
result = vec3(1.0) - exp(-result * Exposure);
}
result = texture(Scene, vTexture).rgb + result;
color = vec4(result, 1);
}

View file

@ -0,0 +1,11 @@
#version 330
layout(location = 1) in vec2 aTex;
uniform mat3 TextureTransform;
out vec2 TransformedTexture;
void vertex() {
TransformedTexture = vec2(TextureTransform * vec3(aTex, 1));
}

View file

@ -0,0 +1,12 @@
#version 330
in vec2 vTexture;
uniform sampler2D Scene;
uniform float Gamma;
layout(location = 0) out vec4 color;
void main() {
color = vec4(pow(texture(Scene, vTexture).rgb, vec3(1 / Gamma)), 1);
}

View file

@ -0,0 +1,15 @@
#version 330
in vec2 vTexture;
uniform sampler2D Scene;
uniform float Exposure;
uniform float Gamma;
layout(location = 0) out vec4 color;
void main() {
vec3 result = vec3(1) - exp(-texture(Scene, vTexture).rgb * Exposure);
color = vec4(pow(result, vec3(1 / Gamma)), 1);
}

View file

@ -0,0 +1,13 @@
#version 330
in vec2 vTexture;
uniform sampler2D renderedTexture;
vec4 GetRenderColor() {
return texture(renderedTexture, vTexture);
}
vec4 GetRenderColorOffset(vec2 offset) {
return texture(renderedTexture, vTexture + offset);
}

View file

@ -0,0 +1,14 @@
#version 330
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTex;
uniform mat4 MVP;
out vec2 vTexture;
void main() {
vTexture = aTex;
gl_Position = MVP * vec4(aPos, 1);
}

View file

@ -0,0 +1,18 @@
#version 330
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTex;
uniform mat4 MVP;
out vec2 vTexture;
void vertex();
void main() {
vTexture = aTex;
gl_Position = MVP * vec4(aPos, 1);
vertex();
}

View file

@ -0,0 +1,57 @@
#region usings
using OpenTK;
using SM.Base.Scene;
using SM.Base.Window;
using SM.OGL.Framebuffer;
#endregion
namespace SM.Base.PostProcess
{
/// <summary>
/// Basis for a post process effect
/// </summary>
public abstract class PostProcessEffect
{
/// <summary>
/// This contains the transformation matrix for post process effects.
/// </summary>
public static Matrix4 Mvp = Matrix4.Identity;
/// <summary>
/// This contains the pipeline this instance is currently active.
/// </summary>
protected RenderPipeline Pipeline;
/// <summary>
/// Initialize the effect.
/// </summary>
/// <param name="pipeline"></param>
public void Initilize(RenderPipeline pipeline)
{
Pipeline = pipeline;
InitProcess();
}
/// <summary>
/// Method, to initialize the shader.
/// </summary>
protected virtual void InitProcess()
{
}
/// <summary>
/// Method to draw the actual effect.
/// </summary>
public abstract void Draw(ColorAttachment source, DrawContext context);
/// <summary>
/// Event, when the scene changed.
/// </summary>
public virtual void SceneChanged(GenericScene scene)
{
}
}
}

View file

@ -0,0 +1,72 @@
#region usings
using System;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Objects.Static;
using SM.Base.Utility;
using SM.OGL.Shaders;
#endregion
namespace SM.Base.PostProcess
{
/// <summary>
/// Specific shader for post processing.
/// </summary>
public class PostProcessShader : GenericShader
{
private static readonly ShaderFile _fragExtensions =
new ShaderFile(AssemblyUtility.ReadAssemblyFile("SM.Base.PostProcess.DefaultFiles.extensions.frag"));
private static readonly ShaderFile _normalVertex =
new ShaderFile(AssemblyUtility.ReadAssemblyFile("SM.Base.PostProcess.DefaultFiles.vertexFile.vert"));
private static readonly string _normalVertexWithExt =
AssemblyUtility.ReadAssemblyFile("SM.Base.PostProcess.DefaultFiles.vertexWithExt.vert");
/// <summary>
/// Creates the shader with the default vertex shader and custom fragment.
/// </summary>
public PostProcessShader(string fragment) : this(_normalVertex,
new ShaderFile(fragment))
{
}
/// <summary>
/// Creates the shader with an vertex extension and custom fragment.
/// </summary>
/// <param name="vertexExt"></param>
/// <param name="fragment"></param>
public PostProcessShader(string vertexExt, string fragment) : this(new ShaderFile(_normalVertexWithExt)
{
GLSLExtensions = new List<ShaderFile> {new ShaderFile(vertexExt)}
}, new ShaderFile(fragment))
{
}
private PostProcessShader(ShaderFile vertex, ShaderFile fragment) : base(
new ShaderFileCollection(vertex, fragment))
{
fragment.GLSLExtensions.Add(_fragExtensions);
}
/// <summary>
/// Draws the shader with special uniforms.
/// </summary>
/// <param name="setUniformAction"></param>
public void Draw(Action<UniformCollection> setUniformAction)
{
Activate();
Plate.Object.Activate();
Uniforms["MVP"].SetMatrix4(PostProcessEffect.Mvp);
setUniformAction(Uniforms);
GL.DrawArrays(PrimitiveType.Quads, 0, 4);
CleanUp();
}
}
}

View file

@ -0,0 +1,39 @@
#region usings
using System.Reflection;
using System.Runtime.InteropServices;
#endregion
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Basis for every SMRenderer")]
[assembly: AssemblyDescription("SMRenderer-Basis functions")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("iedSoftworks")]
[assembly: AssemblyProduct("SM.Base")]
[assembly: AssemblyCopyright("Copyright © iedSoftworks 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8e733844-4204-43e7-b3dc-3913cddabb0d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\packages\SharpFont.4.0.1\build\SharpFont.props" Condition="Exists('..\..\..\packages\SharpFont.4.0.1\build\SharpFont.props')" />
<Import Project="..\..\..\packages\SharpFont.Dependencies.2.6\build\SharpFont.Dependencies.props" Condition="Exists('..\..\..\packages\SharpFont.Dependencies.2.6\build\SharpFont.Dependencies.props')" />
<Import Project="..\..\..\IAmTwo - Kopie\packages\SharpFont.4.0.1\build\SharpFont.props" Condition="Exists('..\..\..\IAmTwo - Kopie\packages\SharpFont.4.0.1\build\SharpFont.props')" />
<Import Project="..\..\..\IAmTwo - Kopie\packages\SharpFont.Dependencies.2.6\build\SharpFont.Dependencies.props" Condition="Exists('..\..\..\IAmTwo - Kopie\packages\SharpFont.Dependencies.2.6\build\SharpFont.Dependencies.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8E733844-4204-43E7-B3DC-3913CDDABB0D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SM.Base</RootNamespace>
<AssemblyName>SM.Base</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
<DocumentationFile>bin\Debug\SM.Base.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="Animation\AnimationCurves.cs" />
<Compile Include="Animation\InterpolationProcess.cs" />
<Compile Include="Controls\Keyboard.cs" />
<Compile Include="Controls\Mouse.cs" />
<Compile Include="Drawing\DrawingBasis.cs" />
<Compile Include="Drawing\GenericTransformation.cs" />
<Compile Include="Drawing\Instance.cs" />
<Compile Include="Drawing\Particles\ParticleInstance.cs" />
<Compile Include="Drawing\ShaderArguments.cs" />
<Compile Include="Drawing\TextureTransformation.cs" />
<Compile Include="Drawing\Text\Font.cs" />
<Compile Include="PostEffects\PostProcessUtility.cs" />
<Compile Include="Scene\ICollectionItem.cs" />
<Compile Include="Scene\IFixedScriptable.cs" />
<Compile Include="Shaders\MaterialShader.cs" />
<Compile Include="Drawing\Particles\ParticleMovement.cs" />
<Compile Include="Drawing\Particles\ParticleDrawingBasis.cs" />
<Compile Include="Shaders\SimpleShader.cs" />
<Compile Include="Types\CVector4.cs" />
<Compile Include="Types\CVectorBase.cs" />
<Compile Include="Utility\IInitializable.cs" />
<Compile Include="Utility\Ray.cs" />
<Compile Include="Utility\Util.cs" />
<Compile Include="Window\Contexts\DrawContext.cs" />
<Compile Include="Window\Contexts\FixedUpdateContext.cs" />
<Compile Include="Window\Contexts\UpdateContext.cs" />
<Compile Include="Window\GLWindow.cs" />
<Compile Include="Log.cs" />
<Compile Include="Objects\InstancedMesh.cs" />
<Compile Include="Objects\Mesh.cs" />
<Compile Include="Objects\Static\AxisHelper.cs" />
<Compile Include="PostEffects\BloomEffect.cs" />
<Compile Include="PostProcess\PostProcessEffect.cs" />
<Compile Include="PostProcess\PostProcessShader.cs" />
<Compile Include="Scene\IScriptable.cs" />
<Compile Include="Scene\IShowCollection.cs" />
<Compile Include="Scene\IShowItem.cs" />
<Compile Include="Drawing\Material.cs" />
<Compile Include="Scene\IBackgroundItem.cs" />
<Compile Include="Scene\GenericItemCollection.cs" />
<Compile Include="Shaders\Extensions\ExtensionManager.cs" />
<Compile Include="SMRenderer.cs" />
<Compile Include="Textures\Texture.cs" />
<Compile Include="Drawing\Text\CharParameter.cs" />
<Compile Include="Drawing\Text\FontCharStorage.cs" />
<Compile Include="Drawing\Text\TextDrawingBasis.cs" />
<Compile Include="Time\Interval.cs" />
<Compile Include="Time\Stopwatch.cs" />
<Compile Include="Time\Timer.cs" />
<Compile Include="Types\CVector1.cs" />
<Compile Include="Types\CVector2.cs" />
<Compile Include="Types\CVector3.cs" />
<Compile Include="Utility\Assembly.cs" />
<Compile Include="Utility\Deltatime.cs" />
<Compile Include="Utility\Randomize.cs" />
<Compile Include="Utility\RotationUtility.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scene\GenericCamera.cs" />
<Compile Include="Scene\GenericScene.cs" />
<Compile Include="Objects\Static\Plate.cs" />
<Compile Include="Window\IGenericWindow.cs" />
<Compile Include="Window\ISetup.cs" />
<Compile Include="Window\RenderPipeline.cs" />
<Compile Include="Window\WindowCode.cs" />
<Compile Include="Window\WindowFlags.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Shaders\Extensions\vertex\basic.vert" />
<EmbeddedResource Include="PostProcess\DefaultFiles\vertexFile.vert" />
<EmbeddedResource Include="PostProcess\DefaultFiles\extensions.frag" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="PostProcess\DefaultFiles\vertexWithExt.vert" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="PostEffects\Shaders\bloom_blur.glsl" />
<EmbeddedResource Include="PostEffects\Shaders\bloom_merge.glsl" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Shaders\SimpleShaderPresets\basic_vertex.glsl" />
<EmbeddedResource Include="Shaders\SimpleShaderPresets\instanced_vertex.glsl" />
<EmbeddedResource Include="PostEffects\Shaders\bloom_merge_vert.glsl" />
<EmbeddedResource Include="PostEffects\Shaders\finalize_hdr.glsl" />
<EmbeddedResource Include="PostEffects\Shaders\finalize_gamma.glsl" />
<EmbeddedResource Include="Shaders\Extensions\fragment\textureGamma.glsl" />
<EmbeddedResource Include="Shaders\Extensions\fragment\noise.glsl" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Window\winIcon.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SM.OGL\SM.OGL.csproj">
<Project>{f604d684-bc1d-4819-88b5-8b5d03a17be0}</Project>
<Name>SM.OGL</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="OpenTK, Version=3.3.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\OpenTK.3.3.1\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpFont, Version=4.0.1.200, Culture=neutral, PublicKeyToken=48add4c483071cdf, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SharpFont.4.0.1\lib\net45\SharpFont.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
</Target>
</Project>

View file

@ -0,0 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">Latest</s:String>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=windows/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=windows_005Ccontexts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=window_005Ccontexts/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -0,0 +1,66 @@
#region usings
using SM.Base.Drawing.Text;
using SM.Base.Objects.Static;
using SM.Base.Shaders;
using SM.Base.Utility;
using SM.Base.Window;
using SM.OGL.Mesh;
#endregion
namespace SM.Base
{
/// <summary>
/// Contains different information about this renderer.
/// </summary>
public class SMRenderer
{
internal const string PostProcessPath = "SM.Base.PostEffects.Shaders";
/// <summary>
/// Defines, how many instances the 'SM_base_vertex_basic'-extension can handle.
/// </summary>
public const int MaxInstances = 32;
/// <summary>
/// The default mesh.
/// </summary>
public static GenericMesh DefaultMesh = Plate.Object;
/// <summary>
/// The default font.
/// </summary>
public static Font DefaultFont;
/// <summary>
/// The default deltatime helper.
/// </summary>
public static Deltatime DefaultDeltatime = new Deltatime();
/// <summary>
/// The default material shader.
/// </summary>
public static MaterialShader DefaultMaterialShader;
/// <summary>
/// The default render pipeline.
/// </summary>
public static RenderPipeline DefaultRenderPipeline;
/// <summary>
/// Shows more information onto the log system.
/// </summary>
public static bool AdvancedDebugging = false;
/// <summary>
/// Current Frame
/// </summary>
public static ulong CurrentFrame { get; internal set; } = 1;
/// <summary>
/// Represents the current active window.
/// </summary>
public static IGenericWindow CurrentWindow { get; internal set; }
}
}

View file

@ -0,0 +1,69 @@
#region usings
using OpenTK;
using SM.Base.Window;
#endregion
namespace SM.Base.Scene
{
/// <summary>
/// Controller for a camera
/// </summary>
public abstract class GenericCamera
{
/// <summary>
/// Exposure defines the exposure to the Scene.
/// </summary>
public float Exposure = 1;
/// <summary>
/// This defines what is up. (Normalized)
/// <para>Default: <see cref="Vector3.UnitY" /></para>
/// </summary>
public Vector3 UpVector { get; set; } = Vector3.UnitY;
/// <summary>
/// Returns the world matrix that is connected to this camera.
/// </summary>
public Matrix4 World { get; protected set; }
/// <summary>
/// Contains the view matrix of this camera.
/// <para>Default: <see cref="Matrix4.Identity" /></para>
/// </summary>
public Matrix4 View { get; protected set; } = Matrix4.Identity;
/// <summary>
/// Represents if the camera is orthographic.
/// </summary>
public abstract bool Orthographic { get; }
/// <summary>
/// Calculates the view matrix.
/// </summary>
/// <returns>The calculated view matrix. Same as <see cref="View" /></returns>
internal void CalculateViewMatrix(IGenericWindow window)
{
View = ViewCalculation(window);
if (WorldCalculation(window, out Matrix4 world)) World = world;
}
/// <summary>
/// This calculates the view matrix.
/// </summary>
/// <returns>
/// The new view matrix. This is the returns for <see cref="CalculateViewMatrix" /> and the next value for
/// <see cref="View" />.
/// </returns>
protected abstract Matrix4 ViewCalculation(IGenericWindow window);
/// <summary>
/// This calculates the world.
/// </summary>
/// <param name="window"></param>
/// <param name="world"></param>
/// <returns></returns>
protected abstract bool WorldCalculation(IGenericWindow window, out Matrix4 world);
}
}

View file

@ -0,0 +1,320 @@
#region usings
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using SM.Base.Drawing;
using SM.Base.Window;
using SM.Base.Window.Contexts;
#endregion
namespace SM.Base.Scene
{
/// <summary>
/// Contains a list of show items.
/// </summary>
public abstract class GenericItemCollection : List<IShowItem>, IShowItem, IShowCollection, IScriptable, IFixedScriptable
{
private readonly List<IScriptable> _scriptableObjects = new List<IScriptable>();
private readonly List<IFixedScriptable> _fixedScriptables = new List<IFixedScriptable>();
/// <summary>
/// Currently active script objects.
/// </summary>
public ReadOnlyCollection<IScriptable> ScriptableObjects =>
new ReadOnlyCollection<IScriptable>(_scriptableObjects);
/// <inheritdoc />
public bool UpdateActive { get; set; } = true;
/// <inheritdoc />
public List<IShowItem> Objects => this;
/// <inheritdoc />
public object Parent { get; set; }
/// <inheritdoc />
public string Name { get; set; } = "Unnamed Item Collection";
/// <inheritdoc />
public ICollection<string> Flags { get; set; } = new List<string> {"collection"};
/// <inheritdoc cref="IShowItem" />
public bool Active { get; set; } = true;
/// <inheritdoc />
public bool RenderActive { get; set; } = true;
/// <inheritdoc />
public virtual void FixedUpdate(FixedUpdateContext context)
{
if (!Active || !UpdateActive) return;
for (int i = 0; i < _fixedScriptables.Count; i++)
{
_fixedScriptables[i].FixedUpdate(context);
}
}
/// <inheritdoc cref="IShowCollection.Draw" />
public virtual void Draw(DrawContext context)
{
if (!Active || !RenderActive) return;
for (var i = 0; i < Objects.Count; i++)
{
if (!this[i].Active || !this[i].RenderActive) continue;
this[i].Draw(context);
}
}
/// <inheritdoc />
public virtual void Update(UpdateContext context)
{
if (!Active || !UpdateActive) return;
for (var i = 0; i < _scriptableObjects.Count; i++)
{
if (!_scriptableObjects[i].Active || !_scriptableObjects[i].UpdateActive) continue;
_scriptableObjects[i].Update(context);
}
}
/// <inheritdoc />
public virtual void OnAdded(object sender)
{
}
/// <inheritdoc />
public virtual void OnRemoved(object sender)
{
}
/// <summary>
/// Adds items to the draw and the script collection, when applicable.
/// </summary>
public void Add(params ICollectionItem[] items)
{
foreach (var item in items)
{
if (item is IShowItem show)
addObject(show);
if (item is IScriptable scriptable)
addScript(scriptable);
if (item is IFixedScriptable fixedScriptable)
addScript(fixedScriptable);
}
}
/// <summary>
/// Adds the object to the collection.
/// </summary>
/// <param name="item"></param>
[Obsolete("Please use Add()")]
public void AddObject(IShowItem item)
{
addObject(item);
}
/// <summary>
/// Adds the script to the collection.
/// </summary>
/// <param name="item"></param>
[Obsolete("Please use Add()")]
public void AddScript(IScriptable item)
{
addScript(item);
}
private void addObject(IShowItem item)
{
base.Add(item);
item.Parent = this;
item.OnAdded(this);
}
private void addScript(IScriptable item)
{
_scriptableObjects.Add(item);
}
private void addScript(IFixedScriptable item)
{
_fixedScriptables.Add(item);
}
/// <summary>
/// Removes an object from the drawing list.
/// <para>If the object is a scriptable object, it will remove the object from that list as well.</para>
/// </summary>
/// <param name="items"></param>
public void Remove(params ICollectionItem[] items)
{
foreach (var item in items)
{
if (item is IShowItem show)
removeObject(show);
if (item is IScriptable scriptable)
removeScript(scriptable);
if (item is IFixedScriptable fixedScriptable)
removeScript(fixedScriptable);
}
}
/// <summary>
/// Remove the object from the draw collection.
/// </summary>
/// <param name="item"></param>
[Obsolete("Please use Remove()")]
public void RemoveObject(IShowItem item)
{
removeObject(item);
}
private void removeObject(IShowItem item)
{
base.Remove(item);
item.Parent = null;
item.OnRemoved(this);
}
/// <summary>
/// Remove the object from the script collection.
/// </summary>
/// <param name="item"></param>
[Obsolete("Please use Remove()")]
public void RemoveScript(IScriptable item)
{
removeScript(item);
}
private void removeScript(IScriptable item)
{
_scriptableObjects.Remove(item);
}
private void removeScript(IFixedScriptable item)
{
_fixedScriptables.Remove(item);
}
/// <summary>
/// Clears the entire collection of everything.
/// </summary>
public new void Clear()
{
foreach (IShowItem item in this.ToArray()) removeObject(item);
foreach (IScriptable scriptable in _scriptableObjects.ToArray()) removeScript(scriptable);
foreach (IFixedScriptable scriptable in _fixedScriptables.ToArray()) removeScript(scriptable);
}
/// <summary>
/// Clears the entire collection of selected systems.
/// <para>If f.E. a object is both visual and scriptable and you clear the visuals, the scriptable-entry will stay.</para>
/// <param name="visuals">Clears visuals</param>
/// <param name="scriptables">Clears scriptables</param>
/// <param name="fixedScriptables">Clears fixed scriptables</param>
/// </summary>
public void Clear(bool visuals = false, bool scriptables = false, bool fixedScriptables = false)
{
if (visuals) foreach (IShowItem item in this.ToArray()) removeObject(item);
if (scriptables) foreach (IScriptable scriptable in _scriptableObjects.ToArray()) removeScript(scriptable);
if (fixedScriptables) foreach (IFixedScriptable scriptable in _fixedScriptables.ToArray()) removeScript(scriptable);
}
/// <summary>
/// Returns all objects in the drawing list.
/// <para>Not reclusive.</para>
/// </summary>
/// <param name="includeCollections">If true, it will add collections as well.</param>
/// <returns></returns>
public ICollection<IShowItem> GetAllItems(bool includeCollections = false)
{
List<IShowItem> items = new List<IShowItem>();
for (var i = 0; i < Count; i++)
{
if (!includeCollections && this[i] is IShowCollection) continue;
items.Add(this[i]);
}
return items;
}
/// <summary>
/// Returns a object with this name or the default, if not available.
/// <para>Not reclusive.</para>
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IShowItem GetItemByName(string name)
{
IShowItem obj = default;
for (var i = 0; i < Count; i++)
if (this[i].Name == name)
{
obj = this[i];
break;
}
return obj;
}
/// <summary>
/// Returns a object with this name or the default if not available.
/// <para>Not reclusive.</para>
/// </summary>
/// <typeparam name="TGetItem">Type of return</typeparam>
public TGetItem GetItemByName<TGetItem>(string name)
where TGetItem : IShowItem
{
return (TGetItem) GetItemByName(name);
}
/// <summary>
/// Returns all object that have this flag.
/// <para>Only in this list.</para>
/// </summary>
public ICollection<IShowItem> GetItemsWithFlag(string flag)
{
var list = new List<IShowItem>();
for (var i = 0; i < Count; i++)
{
var obj = this[i];
if (obj.Flags == null) continue;
if (obj.Flags.Contains(flag)) list.Add(obj);
}
return list;
}
}
/// <summary>
/// Contains a list of show items with transformation.
/// </summary>
/// <typeparam name="TTransformation">The type of transformation.</typeparam>
public abstract class GenericItemCollection<TTransformation> : GenericItemCollection,
IShowTransformItem<TTransformation>
where TTransformation : GenericTransformation, new()
{
/// <summary>
/// Transformation of the collection
/// </summary>
public TTransformation Transform { get; set; } = new TTransformation();
/// <inheritdoc />
public override void Draw(DrawContext context)
{
Transform.LastMaster = context.ModelMatrix;
context.ModelMatrix = Transform.MergeMatrix(context.ModelMatrix);
base.Draw(context);
}
}
}

View file

@ -0,0 +1,272 @@
#region usings
using System;
using System.Collections.Generic;
using SM.Base.Utility;
using SM.Base.Window;
using SM.Base.Window.Contexts;
#endregion
namespace SM.Base.Scene
{
/// <summary>
/// A generic scene, that imports functions for scene control.
/// </summary>
public abstract class GenericScene : IInitializable
{
private IBackgroundItem _background;
private readonly Dictionary<Type, object> _extensions = new Dictionary<Type, object>();
private GenericItemCollection _hud;
private GenericItemCollection _objectCollection;
/// <summary>
/// A collection for cameras to switch easier to different cameras.
/// </summary>
public Dictionary<string, GenericCamera> Cameras = new Dictionary<string, GenericCamera>();
/// <summary>
/// This contains the background.
/// </summary>
protected IBackgroundItem _Background
{
get => _background;
set
{
value.Parent = this;
_background = value;
}
}
/// <summary>
/// Objects inside the scene.
/// </summary>
public GenericItemCollection Objects
{
get => _objectCollection;
set
{
value.Parent = this;
_objectCollection = value;
}
}
/// <summary>
/// This defines the HUD objects.
/// </summary>
public GenericItemCollection HUD
{
get => _hud;
set
{
value.Parent = this;
_hud = value;
}
}
/// <summary>
/// If true, shows a axis helper at (0,0,0)
/// </summary>
public bool ShowAxisHelper { get; set; } = false;
/// <summary>
/// The active camera, that is used if the context doesn't force the viewport camera.
/// <para>If none set, it automaticly uses the viewport camera.</para>
/// </summary>
public GenericCamera Camera { get; set; }
/// <summary>
/// A camera to control the background.
/// </summary>
public GenericCamera BackgroundCamera { get; set; }
/// <summary>
/// A camera to control the HUD.
/// </summary>
public GenericCamera HUDCamera { get; set; }
/// <summary>
/// If true, the scene was already initialized.
/// </summary>
public bool IsInitialized { get; set; }
/// <inheritdoc/>
public virtual void Activate()
{
}
/// <inheritdoc/>
public virtual void Initialization()
{
}
/// <summary>
/// Updates this scene.
/// </summary>
/// <param name="context"></param>
public virtual void Update(UpdateContext context)
{
_objectCollection?.Update(context);
_hud?.Update(context);
}
/// <summary>
/// Executes a fixed update for this scene.
/// </summary>
public virtual void FixedUpdate(FixedUpdateContext context)
{
_objectCollection?.FixedUpdate(context);
_hud?.FixedUpdate(context);
}
/// <summary>
/// Draws this scene.
/// </summary>
public virtual void Draw(DrawContext context)
{
DrawBackground(context);
DrawMainObjects(context);
DrawHUD(context);
DrawDebug(context);
}
/// <summary>
/// Draws only the background.
/// </summary>
/// <param name="context"></param>
public virtual void DrawBackground(DrawContext context)
{
var backgroundDrawContext = context;
backgroundDrawContext.SetCamera(BackgroundCamera);
_Background?.Draw(backgroundDrawContext);
}
/// <summary>
/// Draws only the main objects
/// </summary>
/// <param name="context"></param>
public virtual void DrawMainObjects(DrawContext context)
{
if (!context.Window.ForceViewportCamera && Camera != null) context.SetCamera(Camera);
_objectCollection.Draw(context);
}
/// <summary>
/// Draws only the HUD
/// </summary>
/// <param name="context"></param>
public virtual void DrawHUD(DrawContext context)
{
context.SetCamera(HUDCamera);
_hud?.Draw(context);
}
/// <summary>
/// Draw the debug informations.
/// </summary>
/// <param name="context"></param>
public virtual void DrawDebug(DrawContext context)
{
}
/// <summary>
/// Adds a extension to the scene.
/// </summary>
/// <param name="extension"></param>
public virtual void SetExtension(object extension)
{
_extensions[extension.GetType()] = extension;
}
/// <summary>
/// Gets a extension with the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public virtual T GetExtension<T>() where T : class
{
object ext = _extensions[typeof(T)];
if (ext == null)
{
Log.Write(LogType.Warning,
$"Tried to get the extension '{typeof(T).Name}', that doesn't exist in the scene.");
return null;
}
return (T) ext;
}
/// <summary>
/// This is triggered when the scene gets deactivated.
/// </summary>
public virtual void Deactivate()
{
}
}
/// <summary>
/// A generic scene that imports different functions.
/// </summary>
/// <typeparam name="TCamera">The type of cameras.</typeparam>
/// <typeparam name="TCollection">The type for collections</typeparam>
public abstract class GenericScene<TCamera, TCollection> : GenericScene
where TCamera : GenericCamera, new()
where TCollection : GenericItemCollection, new()
{
/// <summary>
/// Objects inside the scene, but as the collection type.
/// </summary>
public new TCollection Objects
{
get => (TCollection) base.Objects;
set => base.Objects = value;
}
/// <summary>
/// HUD-Objects inside the scene, but as the collection type.
/// </summary>
public new TCollection HUD
{
get
{
base.HUD ??= new TCollection();
return (TCollection) base.HUD;
}
set => base.HUD = value;
}
/// <summary>
/// The active camera, that is used if the context doesn't force the viewport camera.
/// <para>If none set, it automaticly uses the viewport camera.</para>
/// </summary>
public new TCamera Camera
{
get => (TCamera) base.Camera;
set => base.Camera = value;
}
/// <summary>
/// A camera to control the HUD.
/// </summary>
public new TCamera HUDCamera
{
get => (TCamera) base.HUDCamera;
set => base.HUDCamera = value;
}
/// <summary>
/// A camera to control the background.
/// </summary>
public new TCamera BackgroundCamera
{
get => (TCamera) base.BackgroundCamera;
set => base.BackgroundCamera = value;
}
}
}

View file

@ -0,0 +1,9 @@
namespace SM.Base.Scene
{
/// <summary>
/// A iteration of <see cref="IShowItem" /> to reduce clutter.
/// </summary>
public interface IBackgroundItem : IShowItem
{
}
}

View file

@ -0,0 +1,10 @@
namespace SM.Base.Scene
{
/// <summary>
/// Dummy interface for adding items.
/// </summary>
public interface ICollectionItem
{
}
}

View file

@ -0,0 +1,22 @@
using SM.Base.Window.Contexts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SM.Base.Scene
{
/// <summary>
/// This interface allows for a object to implerment a fixed update.
/// </summary>
public interface IFixedScriptable : ICollectionItem
{
/// <summary>
/// Executes a fixed update.
/// </summary>
/// <param name="context"></param>
void FixedUpdate(FixedUpdateContext context);
}
}

View file

@ -0,0 +1,29 @@
#region usings
using SM.Base.Window;
#endregion
namespace SM.Base.Scene
{
/// <summary>
/// Defines a object as script.
/// </summary>
public interface IScriptable : ICollectionItem
{
/// <summary>
/// If not active, ItemCollections will ignore them.
/// </summary>
bool Active { get; set; }
/// <summary>
/// If not active, ItemCollections will ignore them.
/// </summary>
bool UpdateActive { get; set; }
/// <summary>
/// Updates the object.
/// </summary>
void Update(UpdateContext context);
}
}

View file

@ -0,0 +1,26 @@
#region usings
using System.Collections.Generic;
using SM.Base.Window;
#endregion
namespace SM.Base.Scene
{
/// <summary>
/// Adds functions, that is required for a collection.
/// </summary>
public interface IShowCollection
{
/// <summary>
/// The object collection.
/// </summary>
List<IShowItem> Objects { get; }
/// <summary>
/// This draws the objects in the <see cref="Objects" /> list.
/// </summary>
/// <param name="context">The context how the objects need to be drawn.</param>
void Draw(DrawContext context);
}
}

View file

@ -0,0 +1,90 @@
#region usings
using System.Collections.Generic;
using SM.Base.Drawing;
using SM.Base.Window;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Scene
{
/// <summary>
/// Adds requirements to object, to be properly used as a update and/or draw item.
/// </summary>
public interface IShowItem : ICollectionItem
{
/// <summary>
/// Parent of the object.
/// </summary>
object Parent { get; set; }
/// <summary>
/// Contains the name for the object.
/// </summary>
string Name { get; set; }
/// <summary>
/// Contains specific flags for the object.
/// </summary>
ICollection<string> Flags { get; set; }
/// <summary>
/// If true it will ignore the object.
/// </summary>
bool Active { get; set; }
/// <summary>
/// Íf true it will ignore the object when rendering.
/// </summary>
bool RenderActive { get; set; }
/// <summary>
/// Tells the object to draw its object.
/// </summary>
/// <param name="context"></param>
void Draw(DrawContext context);
/// <summary>
/// Action, that is called, when the object was added to a GenericItemCollection.
/// </summary>
void OnAdded(object sender);
/// <summary>
/// Action, that is called, when the object was removed from a GenericItemCollection.
/// </summary>
void OnRemoved(object sender);
}
/// <summary>
/// Interface to implement transformation.
/// </summary>
/// <typeparam name="TTransform"></typeparam>
public interface ITransformItem<TTransform>
where TTransform : GenericTransformation
{
/// <summary>
/// Controls the transformation of the object.
/// </summary>
TTransform Transform { get; set; }
}
/// <summary>
/// Merges <see cref="IShowItem"/> and <see cref="ITransformItem{TTransform}"/>.
/// </summary>
/// <typeparam name="TTransform"></typeparam>
public interface IShowTransformItem<TTransform> : IShowItem, ITransformItem<TTransform>
where TTransform : GenericTransformation
{
}
/// <summary>
/// Interface to implement models in the object.
/// </summary>
public interface IModelItem
{
/// <summary>
/// The mesh the rendering should use.
/// </summary>
GenericMesh Mesh { get; set; }
}
}

View file

@ -0,0 +1,19 @@
#region usings
using SM.OGL.Shaders;
#endregion
namespace SM.Base.Shaders.Extensions
{
internal class ExtensionManager
{
internal static void InitExtensions()
{
ShaderExtensions.AddAssemblyExtensions("SM_base", "SM.Base.Shaders.Extensions");
ShaderExtensions.Extensions["SM_base_vertex_basic"].StringOverrides["instanceMax"] =
SMRenderer.MaxInstances.ToString();
}
}
}

View file

@ -0,0 +1,71 @@
#version 330
// Permute
vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
// Classic Perlin 2D Noise
// by Stefan Gustavson
vec2 fade(vec2 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}
float ClassicPerlinNoise(vec2 P){
vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
Pi = mod(Pi, 289.0); // To avoid truncation effects in permutation
vec4 ix = Pi.xzxz;
vec4 iy = Pi.yyww;
vec4 fx = Pf.xzxz;
vec4 fy = Pf.yyww;
vec4 i = permute(permute(ix) + iy);
vec4 gx = 2.0 * fract(i * 0.0243902439) - 1.0; // 1/41 = 0.024...
vec4 gy = abs(gx) - 0.5;
vec4 tx = floor(gx + 0.5);
gx = gx - tx;
vec2 g00 = vec2(gx.x,gy.x);
vec2 g10 = vec2(gx.y,gy.y);
vec2 g01 = vec2(gx.z,gy.z);
vec2 g11 = vec2(gx.w,gy.w);
vec4 norm = 1.79284291400159 - 0.85373472095314 *
vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11));
g00 *= norm.x;
g01 *= norm.y;
g10 *= norm.z;
g11 *= norm.w;
float n00 = dot(g00, vec2(fx.x, fy.x));
float n10 = dot(g10, vec2(fx.y, fy.y));
float n01 = dot(g01, vec2(fx.z, fy.z));
float n11 = dot(g11, vec2(fx.w, fy.w));
vec2 fade_xy = fade(Pf.xy);
vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
return 2.3 * n_xy;
}
// Simplex 2D noise
float SimplexNoise(vec2 v){
const vec4 C = vec4(0.211324865405187, 0.366025403784439,
-0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod(i, 289.0);
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}

View file

@ -0,0 +1,8 @@
#version 330
uniform float Gamma;
vec4 texture2DGamma(sampler2D s, vec2 P) {
vec4 tex = texture2D(s, P);
return vec4(pow(tex.rgb, vec3(Gamma)), tex.a);
}

View file

@ -0,0 +1,35 @@
#version 330
#define maxInstances //!instanceMax
struct Instance {
mat4 ModelMatrix;
vec2 TextureOffset;
vec2 TextureScale;
};
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTex;
layout(location = 3) in vec4 aColor;
uniform mat4 MVP;
uniform Instance[maxInstances] Instances;
uniform bool HasVColor;
out vec2 vTexture;
out vec4 vColor;
out vec3 FragPos;
void ApplyTexModifier() {
vTexture = aTex * Instances[gl_InstanceID].TextureScale + Instances[gl_InstanceID].TextureOffset;
}
void CheckVertexColor() {
if (HasVColor) vColor = aColor;
else vColor = vec4(1);
}
void ApplyModelTransformation() {
gl_Position = MVP * Instances[gl_InstanceID].ModelMatrix * vec4(aPos, 1);
FragPos = vec3(vec4(aPos, 1));
}

View file

@ -0,0 +1,78 @@
#region usings
using OpenTK.Graphics.OpenGL4;
using SM.Base.Window;
using SM.OGL.Mesh;
using SM.OGL.Shaders;
#endregion
namespace SM.Base.Shaders
{
/// <summary>
/// A general class to work with material shaders properly.
/// </summary>
public abstract class MaterialShader : GenericShader
{
static bool _canLineWidth = true;
/// <inheritdoc />
protected MaterialShader(string combinedData) : base(combinedData)
{
}
/// <inheritdoc />
protected MaterialShader(string vertex, string fragment) : base(vertex, fragment)
{
}
/// <inheritdoc />
protected MaterialShader(ShaderFileCollection shaderFileFiles) : base(shaderFileFiles)
{
}
/// <summary>
/// Prepares the context for the drawing.
/// </summary>
/// <param name="context">The context</param>
public virtual void Draw(DrawContext context)
{
context.Shader.Activate();
context.Mesh.Activate();
if (_canLineWidth)
{
try
{
if (context.Material.ShaderArguments.ContainsKey("LineWidth"))
GL.LineWidth((float)context.Material.ShaderArguments["LineWidth"]);
}
catch
{
_canLineWidth = false;
}
}
if (context.Material.Blending)
{
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
}
else
{
GL.Disable(EnableCap.Blend);
}
DrawProcess(context);
CleanUp();
}
/// <summary>
/// Draws the context.
/// </summary>
/// <param name="context"></param>
protected abstract void DrawProcess(DrawContext context);
}
}

View file

@ -0,0 +1,147 @@
#region usings
using System;
using System.Collections.Generic;
using SM.Base.Utility;
using SM.Base.Window;
using SM.OGL.Shaders;
#endregion
namespace SM.Base.Shaders
{
/// <summary>
/// Allows for simple creation of shaders.
/// </summary>
public class SimpleShader : MaterialShader
{
/// <summary>
/// Vertex files that are stored in this dictionary can be used as vertex presets.
/// </summary>
public static Dictionary<string, Tuple<ShaderFile, Action<UniformCollection, DrawContext>>> VertexFiles =
new Dictionary<string, Tuple<ShaderFile, Action<UniformCollection, DrawContext>>>();
private readonly string _vertexPreset;
/// <summary>
/// Stores the function that sets the uniforms.
/// </summary>
public Action<UniformCollection, DrawContext> SetUniform;
static SimpleShader()
{
VertexFiles.Add("basic", new Tuple<ShaderFile, Action<UniformCollection, DrawContext>>(
new ShaderFile(
AssemblyUtility.ReadAssemblyFile("SM.Base.Shaders.SimpleShaderPresets.basic_vertex.glsl"))
{
StringOverrides = {["extension"] = "0"}
},
BasicSetUniforms
));
VertexFiles.Add("E_basic", new Tuple<ShaderFile, Action<UniformCollection, DrawContext>>(
new ShaderFile(
AssemblyUtility.ReadAssemblyFile("SM.Base.Shaders.SimpleShaderPresets.basic_vertex.glsl"))
{
StringOverrides = {["extension"] = "1"}
},
BasicSetUniforms
));
VertexFiles.Add("instanced", new Tuple<ShaderFile, Action<UniformCollection, DrawContext>>(
new ShaderFile(
AssemblyUtility.ReadAssemblyFile("SM.Base.Shaders.SimpleShaderPresets.instanced_vertex.glsl"))
{
StringOverrides = {["instanceMax"] = SMRenderer.MaxInstances.ToString(), ["extension"] = "0"}
},
InstancedSetUniforms
));
VertexFiles.Add("E_instanced", new Tuple<ShaderFile, Action<UniformCollection, DrawContext>>(
new ShaderFile(
AssemblyUtility.ReadAssemblyFile("SM.Base.Shaders.SimpleShaderPresets.instanced_vertex.glsl"))
{
StringOverrides = {["instanceMax"] = SMRenderer.MaxInstances.ToString(), ["extension"] = "0"}
},
InstancedSetUniforms
));
}
/// <summary>
/// Creates a simple shader.
/// </summary>
/// <param name="vertexPreset">The vertex preset.</param>
/// <param name="fragment">The fragment shader</param>
/// <param name="setUniform">The uniform function.</param>
public SimpleShader(string vertexPreset, string fragment, Action<UniformCollection, DrawContext> setUniform) :
base(new ShaderFileCollection(VertexFiles[vertexPreset].Item1, new ShaderFile(fragment)))
{
_vertexPreset = vertexPreset;
SetUniform = setUniform;
}
/// <summary>
/// Creates a simple shader with a extension.
/// </summary>
/// <param name="vertexPreset">The vertex preset.</param>
/// <param name="vertexExtension">The vertex extension shader</param>
/// <param name="fragment">The fragment shader</param>
/// <param name="setUniform">The uniform function.</param>
public SimpleShader(string vertexPreset, string vertexExtension, string fragment,
Action<UniformCollection, DrawContext> setUniform) : base(new ShaderFileCollection(
new[] {VertexFiles["E_" + vertexPreset].Item1, new ShaderFile(vertexExtension)},
new[] {new ShaderFile(fragment)}))
{
_vertexPreset = vertexPreset;
SetUniform = setUniform;
}
private static void BasicSetUniforms(UniformCollection uniforms, DrawContext context)
{
// Vertex Uniforms
uniforms["MVP"]
.SetMatrix4(context.Instances[0].ModelMatrix * context.ModelMatrix * context.View * context.World);
uniforms["MasterTextureMatrix"].SetMatrix3(context.Instances[0].TextureMatrix * context.TextureMatrix);
uniforms["HasVColor"]
.SetUniform1(context.Mesh.Attributes.Has("color"));
DrawObject(context.ForcedType.GetValueOrDefault(context.Mesh.PrimitiveType), context.Mesh);
}
private static void InstancedSetUniforms(UniformCollection uniforms, DrawContext context)
{
uniforms["MVP"].SetMatrix4(context.ModelMatrix * context.View * context.World);
uniforms["MasterTextureMatrix"].SetMatrix3(context.TextureMatrix);
uniforms["HasVColor"]
.SetUniform1(context.Mesh.Attributes.Has("color"));
UniformArray instances = uniforms.GetArray("Instances");
int shaderInstanceI = 0;
for (int i = 0; i < context.Instances.Count; i++)
{
if (shaderInstanceI > instances.Length - 1)
{
DrawObject(context.Mesh, instances.Length);
shaderInstanceI = 0;
}
var shaderInstance = instances[shaderInstanceI];
var instance = context.Instances[i];
if (instance == null) continue;
shaderInstance["ModelMatrix"].SetMatrix4(instance.ModelMatrix);
shaderInstance["TextureMatrix"].SetMatrix3(instance.TextureMatrix);
shaderInstanceI++;
}
DrawObject(context.ForcedType.GetValueOrDefault(context.Mesh.PrimitiveType), context.Mesh, shaderInstanceI);
}
/// <inheritdoc />
protected override void DrawProcess(DrawContext context)
{
SetUniform?.Invoke(Uniforms, context);
VertexFiles[_vertexPreset].Item2(Uniforms, context);
}
}
}

View file

@ -0,0 +1,32 @@
#version 330
#define SM_SIMPLE_EXTENSION //!extension
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_Texture;
layout(location = 3) in vec4 a_Color;
uniform bool HasVColor;
uniform mat4 MVP;
uniform mat3 MasterTextureMatrix;
out vec3 v_VertexPosition;
out vec2 v_TexCoords;
out vec4 v_Color;
#if (SM_SIMPLE_EXTENSION == 1)
void v_Extension();
#endif
void main() {
v_Color = vec4(1);
if (HasVColor) v_Color = a_Color;
v_TexCoords = vec2(MasterTextureMatrix * vec3(a_Texture, 1));
v_VertexPosition = a_Position;
gl_Position = MVP * vec4(a_Position, 1);
#if (SM_SIMPLE_EXTENSION == 1)
v_Extension();
#endif
}

View file

@ -0,0 +1,39 @@
#version 330
#define maxInstances //!instanceMax
#define SM_SIMPLE_EXTENSION //!extension
struct Instance {
mat4 ModelMatrix;
mat3 TextureMatrix;
};
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_Texture;
layout(location = 3) in vec4 a_Color;
uniform mat4 MVP;
uniform mat3 MasterTextureMatrix;
uniform Instance[maxInstances] Instances;
uniform bool HasVColor;
out vec3 v_VertexPosition;
out vec2 v_TexCoords;
out vec4 v_Color;
#if (SM_SIMPLE_EXTENSION == 1)
void v_Extension();
#endif
void main() {
v_Color = vec4(1);
if (HasVColor) v_Color = a_Color;
v_TexCoords = vec2(MasterTextureMatrix * Instances[gl_InstanceID].TextureMatrix * vec3(a_Texture, 1));
v_VertexPosition = a_Position;
gl_Position = MVP * Instances[gl_InstanceID].ModelMatrix * vec4(a_Position, 1);
#if (SM_SIMPLE_EXTENSION == 1)
v_Extension();
#endif
}

View file

@ -0,0 +1,170 @@
#region usings
using System.Drawing;
using System.Drawing.Imaging;
using OpenTK.Graphics.OpenGL4;
using SM.OGL.Texture;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
#endregion
namespace SM.Base.Textures
{
/// <summary>
/// Texture that can be drawn to an object.
/// </summary>
public class Texture : TextureBase
{
static HintMode _hintMode;
/// <summary>
/// Defines the texture quailty.
/// <para>It won't change already compiled textures!</para>
/// </summary>
public static HintMode TextureQuality
{
get => _hintMode;
set
{
_hintMode = value;
GL.Hint(HintTarget.TextureCompressionHint, value);
}
}
private int? _height;
private int? _width;
/// <summary>
/// The unpack alignment for this texture.
/// </summary>
public int UnpackAlignment = 4;
/// <summary>
/// Decides if the bitmap will automatically dispose itself.
/// </summary>
public bool AutoDispose = false;
/// <summary>
/// The texture as bitmap.
/// </summary>
public Bitmap Map;
/// <summary>
/// Empty constructor
/// </summary>
protected Texture()
{
}
/// <summary>
/// Creates a texture with only the map.
/// <para>Sets the filter to Linear and WrapMode to Repeat.</para>
/// </summary>
/// <param name="map">The map</param>
public Texture(Bitmap map) : this(map, TextureMinFilter.Linear, TextureWrapMode.Repeat)
{
}
/// <summary>
/// Creates the texture.
/// </summary>
/// <param name="map">The texture map</param>
/// <param name="filter">The filter</param>
/// <param name="wrapMode">The wrap mode</param>
public Texture(Bitmap map, TextureMinFilter filter, TextureWrapMode wrapMode)
{
Map = map;
Aspect = (float) map.Width / map.Height;
Filter = filter;
WrapMode = wrapMode;
}
/// <inheritdoc />
public override int Width
{
get => _width ?? Map.Width;
protected set => _width = value;
}
/// <inheritdoc />
public override int Height
{
get => _height ?? Map.Height;
protected set => _height = value;
}
/// <summary>
/// Aspect ratio of Width and Height of the texture
/// </summary>
public float Aspect { get; }
/// <inheritdoc />
public override void Compile()
{
base.Compile();
_id = GenerateTexture(Map, Filter, WrapMode, AutoDispose, UnpackAlignment);
}
/// <inheritdoc />
public override void Dispose()
{
base.Dispose();
GL.DeleteTexture(this);
}
/// <summary>
/// Generates a OpenGL-texture.
/// </summary>
/// <param name="map">The texture as bitmap</param>
/// <param name="filter">The filter</param>
/// <param name="wrapMode">The wrap mode</param>
/// <param name="dispose">Auto dispose of the bitmap? Default: false</param>
/// <param name="unpackAlignment">The unpack alignment for this texture.</param>
/// <returns></returns>
public static int GenerateTexture(Bitmap map, TextureMinFilter filter, TextureWrapMode wrapMode,
bool dispose = false, int unpackAlignment = 4)
{
var id = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, id);
GL.PixelStore(PixelStoreParameter.UnpackAlignment, unpackAlignment);
var data = map.LockBits(new Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadOnly,
map.PixelFormat);
var transparenz = map.PixelFormat == PixelFormat.Format32bppArgb;
GL.TexImage2D(TextureTarget.Texture2D, 0,
transparenz ? PixelInternalFormat.CompressedRgba : PixelInternalFormat.CompressedRgb,
data.Width, data.Height, 0,
transparenz ? OpenTK.Graphics.OpenGL4.PixelFormat.Bgra : OpenTK.Graphics.OpenGL4.PixelFormat.Bgr,
PixelType.UnsignedByte, data.Scan0);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) filter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) filter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) wrapMode);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) wrapMode);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, 0);
map.UnlockBits(data);
if (dispose) map.Dispose();
return id;
}
/// <summary>
/// Converts a bitmap to a texture.
/// </summary>
public static implicit operator Texture(Bitmap map)
{
return new Texture(map);
}
}
}

View file

@ -0,0 +1,32 @@
#region usings
using System;
using SM.Base.Window;
#endregion
namespace SM.Base.Time
{
/// <summary>
/// Performs intervals.
/// </summary>
public class Interval : Timer
{
/// <inheritdoc />
public Interval(float seconds) : base(seconds)
{
}
/// <inheritdoc />
public Interval(TimeSpan timeSpan) : base(timeSpan)
{
}
/// <inheritdoc />
protected override void Stopping(UpdateContext context)
{
TriggerEndAction(context);
Reset();
}
}
}

View file

@ -0,0 +1,128 @@
#region usings
using System;
using System.Collections.Generic;
using SM.Base.Window;
#endregion
namespace SM.Base.Time
{
/// <summary>
/// Represents a stopwatch.
/// </summary>
public class Stopwatch
{
private static readonly List<Stopwatch> _activeStopwatches = new List<Stopwatch>();
private bool _paused;
/// <summary>
/// If true, the stopwatch was started.
/// <para>This doesn't changed when paused.</para>
/// </summary>
public bool Active { get; private set; }
/// <summary>
/// Gets/Sets if the stopwatch is paused.
/// </summary>
public bool Paused
{
get => _paused;
set
{
if (value)
Pause();
else
Resume();
}
}
/// <summary>
/// If true, the stopwatch is active and not paused... (who would have guessed...)
/// </summary>
public bool Running => Active && !Paused;
/// <summary>
/// Contains how much time already has passed. (in seconds)
/// </summary>
public float Elapsed { get; protected set; }
/// <summary>
/// Contains the TimeSpan of how much time already passed.
/// </summary>
public TimeSpan ElapsedSpan { get; protected set; }
/// <summary>
/// This event gets triggered every tick.
/// </summary>
public event Action<Stopwatch, UpdateContext> Tick;
/// <summary>
/// Starts the stopwatch.
/// </summary>
public virtual void Start()
{
if (Active) return;
_activeStopwatches.Add(this);
Active = true;
}
/// <summary>
/// Performs a tick.
/// </summary>
/// <param name="context"></param>
private protected virtual void Ticking(UpdateContext context)
{
Elapsed += context.Deltatime;
ElapsedSpan = TimeSpan.FromSeconds(Elapsed);
Tick?.Invoke(this, context);
}
/// <summary>
/// Resumes the timer.
/// </summary>
protected virtual void Resume()
{
_paused = false;
}
/// <summary>
/// Pauses the timer.
/// </summary>
protected virtual void Pause()
{
_paused = true;
}
/// <summary>
/// Stops the stopwatch.
/// </summary>
public virtual void Stop()
{
if (!Active) return;
Active = false;
_activeStopwatches.Remove(this);
}
/// <summary>
/// Resets the stopwatch.
/// </summary>
public void Reset()
{
Elapsed = 0;
}
internal static void PerformTicks(UpdateContext context)
{
for (var i = 0; i < _activeStopwatches.Count; i++)
{
if (_activeStopwatches[i].Paused) continue;
_activeStopwatches[i].Ticking(context);
}
}
}
}

View file

@ -0,0 +1,81 @@
#region usings
using System;
using SM.Base.Window;
#endregion
namespace SM.Base.Time
{
/// <summary>
/// Timer-System
/// </summary>
public class Timer : Stopwatch
{
/// <summary>
/// Creates a timer with specified seconds.
/// </summary>
/// <param name="seconds"></param>
public Timer(float seconds)
{
Target = seconds;
}
/// <summary>
/// Creates a timer with a time span.
/// </summary>
/// <param name="timeSpan"></param>
public Timer(TimeSpan timeSpan)
{
Target = (float) timeSpan.TotalSeconds;
}
/// <summary>
/// The target time in seconds.
/// </summary>
public float Target { get; set; }
/// <summary>
/// The already elapsed time but normalized to the target.
/// </summary>
public float ElapsedNormalized { get; private set; }
/// <summary>
/// The event, that is triggered when the timer stops.
/// </summary>
public event Action<Timer, UpdateContext> End;
/// <inheritdoc />
public override void Start()
{
base.Start();
Reset();
}
private protected override void Ticking(UpdateContext context)
{
base.Ticking(context);
ElapsedNormalized = Math.Min(Elapsed / Target, 1);
if (ElapsedNormalized >= 1) Stopping(context);
}
/// <summary>
/// Occurs, when the timer tries to stop.
/// </summary>
protected virtual void Stopping(UpdateContext context)
{
TriggerEndAction(context);
Stop();
}
/// <summary>
/// This will trigger <see cref="End" />
/// </summary>
/// <param name="context"></param>
protected void TriggerEndAction(UpdateContext context)
{
End?.Invoke(this, context);
}
}
}

View file

@ -0,0 +1,109 @@
#region usings
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using OpenTK;
using SM.Base.Animation;
#endregion
namespace SM.Base.Types
{
/// <summary>
/// A One-dimensional Vector (also known as <see cref="float" />), in a class.
/// </summary>
public class CVector1 : CVectorBase
{
/// <summary>
/// Creates a class vector
/// </summary>
/// <param name="x">X-Component</param>
public CVector1(float x)
{
X = x;
}
/// <summary>
/// X - Component
/// </summary>
public float X { get; set; }
/// <summary>
/// Interpolates the motion to the target.
/// </summary>
/// <param name="duration">How long the interpolation should take.</param>
/// <param name="to">The value it should interpolate.</param>
/// <param name="interpolationCurve">The curve how he interpolates. Preset values can be found under <see cref="AnimationCurves"/>. Default: <see cref="AnimationCurves.Linear"/></param>
/// <returns>A handle to control the interpolation process.</returns>
public InterpolationProcess Interpolate(TimeSpan duration, float to, BezierCurve? interpolationCurve = null)
{
InterpolationProcess process = new InterpolationProcess(this, duration, ConvertToVector4(), new Vector4(to, 0, 0, 0), interpolationCurve.GetValueOrDefault(AnimationCurves.Linear));
process.Start();
return process;
}
/// <summary>
/// Sets the X-Component.
/// </summary>
public virtual void Set(float uniform, bool triggerChanged = true)
{
X = uniform;
if (triggerChanged) TriggerChanged();
}
/// <inheritdoc />
public override void SetRaw(params float[] parameters)
{
X = parameters[0];
}
/// <summary>
/// Adds the value to the components.
/// </summary>
public virtual void Add(float uniform, bool triggerChanged = true)
{
X += uniform;
if (triggerChanged) TriggerChanged();
}
/// <summary>
/// Conversion from <see cref="float" /> to One-dimensional Vector.
/// </summary>
/// <returns></returns>
protected override float GetLengthProcess()
{
return X * X;
}
/// <summary>
/// Normalizes the vector.
/// </summary>
/// <param name="length"></param>
protected override void NormalizationProcess(float length)
{
X *= length;
}
/// <inheritdoc />
protected override Vector4 ConvertToVector4()
{
return new Vector4(X, 0, 0, 0);
}
/// <inheritdoc />
public override string ToString()
{
return X.ToString();
}
/// <summary>
/// Conversion into <see cref="float" />
/// </summary>
public static implicit operator float(CVector1 vector1)
{
return vector1.X;
}
}
}

View file

@ -0,0 +1,160 @@
#region usings
using System;
using OpenTK;
using SM.Base.Animation;
#endregion
namespace SM.Base.Types
{
/// <summary>
/// A two-dimensional vector.
/// </summary>
public class CVector2 : CVector1
{
/// <summary>
/// Creates a vector, where each component is the same value.
/// </summary>
/// <param name="uniform">The Value</param>
public CVector2(float uniform) : base(uniform)
{
Y = uniform;
}
/// <summary>
/// Creates a vector
/// </summary>
public CVector2(float x, float y) : base(x)
{
Y = y;
}
/// <summary>
/// Y-component
/// </summary>
public float Y { get; set; }
/// <summary>
/// Interpolates the motion to the target.
/// </summary>
/// <param name="duration">How long the interpolation should take.</param>
/// <param name="to">The value it should interpolate.</param>
/// <param name="interpolationCurve">The curve how he interpolates.
/// <para>When creating a curve, its recommended the Y-component is always between 0 -> 1. But it could make cool effects if not...</para>
/// <para>Preset curves can be found under <see cref="AnimationCurves"/>.</para>
/// <para>Default: <see cref="AnimationCurves.Linear"/></para>
/// </param>
/// <returns>A handle to control the interpolation process.</returns>
public InterpolationProcess Interpolate(TimeSpan duration, Vector2 to, BezierCurve? interpolationCurve = null)
{
InterpolationProcess process = new InterpolationProcess(this, duration, ConvertToVector4(), new Vector4(to), interpolationCurve.GetValueOrDefault(AnimationCurves.Linear));
process.Start();
return process;
}
/// <inheritdoc />
public override string ToString()
{
return "{"+X+"; "+Y+"}";
}
/// <summary>
/// Sets each component to the same value
/// </summary>
public override void Set(float uniform, bool triggerChanged = true)
{
Y = uniform;
base.Set(uniform, triggerChanged);
}
/// <summary>
/// Sets each component to the <see cref="Vector2" /> counter-part.
/// </summary>
public void Set(Vector2 vector, bool triggerChanged = true)
{
Set(vector.X, vector.Y, triggerChanged);
}
/// <summary>
/// Sets the a own value to each component.
/// </summary>
public void Set(float x, float y, bool triggerChanged = true)
{
Y = y;
base.Set(x, triggerChanged);
}
/// <inheritdoc />
public override void SetRaw(params float[] parameters)
{
base.SetRaw(parameters);
Y = parameters[1];
}
/// <inheritdoc />
public override void Add(float uniform, bool triggerChanged = true)
{
Y += uniform;
base.Add(uniform, triggerChanged);
}
/// <summary>
/// Adds <see cref="Vector2"/> to the CVector.
/// </summary>
/// <param name="vector"></param>
/// <param name="triggerChanged">If false, the event Changed doesn't gets triggered </param>
public void Add(Vector2 vector, bool triggerChanged = true)
{
Add(vector.X, vector.Y, triggerChanged);
}
/// <summary>
/// Adds the values to the CVector.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="triggerChanged">If false, the event Changed doesn't gets triggered </param>
public void Add(float x, float y, bool triggerChanged = true)
{
Y += y;
base.Add(x, triggerChanged);
}
/// <inheritdoc />
protected override float GetLengthProcess()
{
return base.GetLengthProcess() + Y * Y;
}
/// <inheritdoc />
protected override void NormalizationProcess(float length)
{
base.NormalizationProcess(length);
Y *= length;
}
/// <inheritdoc />
protected override Vector4 ConvertToVector4()
{
return new Vector4(X,Y, 0, 0);
}
/// <summary>
/// Converts to <see cref="Vector2" />
/// </summary>
public static implicit operator Vector2(CVector2 vector2)
{
return new Vector2(vector2.X, vector2.Y);
}
/// <summary>
/// Converts from <see cref="Vector2" /> to <see cref="CVector2" />.
/// </summary>
public static implicit operator CVector2(Vector2 vector2)
{
return new CVector2(vector2.X, vector2.Y);
}
}
}

View file

@ -0,0 +1,155 @@
#region usings
using System;
using OpenTK;
using SM.Base.Animation;
#endregion
namespace SM.Base.Types
{
/// <summary>
/// A three-dimensional vector.
/// </summary>
public class CVector3 : CVector2
{
/// <summary>
/// Creates a vector, where each component is the same value.
/// </summary>
/// <param name="uniform">The Value</param>
public CVector3(float uniform) : base(uniform)
{
Z = uniform;
}
/// <summary>
/// Creates a vector
/// </summary>
public CVector3(float x, float y, float z) : base(x, y)
{
Z = z;
}
/// <summary>
/// Z-component
/// </summary>
public float Z { get; set; }
/// <summary>
/// Interpolates the motion to the target.
/// </summary>
/// <param name="duration">How long the interpolation should take.</param>
/// <param name="to">The value it should interpolate.</param>
/// <param name="interpolationCurve">The curve how he interpolates. Preset values can be found under <see cref="AnimationCurves"/>. Default: <see cref="AnimationCurves.Linear"/></param>
/// <returns>A handle to control the interpolation process.</returns>
public InterpolationProcess Interpolate(TimeSpan duration, Vector3 to, BezierCurve? interpolationCurve = null)
{
InterpolationProcess process = new InterpolationProcess(this, duration, ConvertToVector4(), new Vector4(to, 0), interpolationCurve.GetValueOrDefault(AnimationCurves.Linear));
process.Start();
return process;
}
/// <inheritdoc />
public override string ToString()
{
return "{" + X + "; " + Y + "; "+Z+"}";
}
/// <inheritdoc />
public override void Set(float uniform, bool triggerChanged = true)
{
Z = uniform;
base.Set(uniform, triggerChanged);
}
/// <summary>
/// Sets the a own value to each component.
/// </summary>
public void Set(float x, float y, float z, bool triggerChanged = true)
{
Z = z;
base.Set(x, y, triggerChanged);
}
/// <summary>
/// Sets each component to the <see cref="Vector3" /> counter-part.
/// </summary>
public void Set(Vector3 vector, bool triggerChanged = true)
{
Set(vector.X, vector.Y, vector.Z, triggerChanged);
}
/// <inheritdoc />
public override void SetRaw(params float[] parameters)
{
base.SetRaw(parameters);
Z = parameters[2];
}
/// <inheritdoc />
public override void Add(float uniform, bool triggerChanged = true)
{
Z += uniform;
base.Add(uniform, triggerChanged);
}
/// <summary>
/// Adds a <see cref="Vector3"/> to the CVector.
/// </summary>
/// <param name="vector"></param>
/// <param name="triggerChanged">If false, the event Changed doesn't gets triggered </param>
public void Add(Vector3 vector, bool triggerChanged = true)
{
Add(vector.X, vector.Y, vector.Z, triggerChanged);
}
/// <summary>
/// Adds the values to the CVector.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
/// <param name="triggerChanged">If false, the event Changed doesn't gets triggered </param>
public void Add(float x, float y, float z, bool triggerChanged = true)
{
Z += z;
base.Add(x, y, triggerChanged);
}
/// <inheritdoc />
protected override float GetLengthProcess()
{
return base.GetLengthProcess() + Z * Z;
}
/// <inheritdoc />
protected override void NormalizationProcess(float length)
{
base.NormalizationProcess(length);
Z *= length;
}
/// <inheritdoc />
protected override Vector4 ConvertToVector4()
{
return new Vector4(X, Y, Z, 0);
}
/// <summary>
/// Converts to <see cref="Vector3" />
/// </summary>
public static implicit operator Vector3(CVector3 vector)
{
return new Vector3(vector.X, vector.Y, vector.Z);
}
/// <summary>
/// Converts from <see cref="Vector3" /> to <see cref="CVector3" />.
/// </summary>
public static implicit operator CVector3(Vector3 vector)
{
return new CVector3(vector.X, vector.Y, vector.Z);
}
}
}

View file

@ -0,0 +1,138 @@
using System;
using OpenTK;
using SM.Base.Animation;
namespace SM.Base.Types
{
/// <inheritdoc />
public class CVector4 : CVector3
{
/// <summary>
/// The W-component.
/// </summary>
public float W { get; set; }
/// <inheritdoc />
public CVector4(float uniform) : base(uniform)
{
W = uniform;
}
/// <inheritdoc />
public CVector4(float x, float y, float z, float w) : base(x, y, z)
{
W = w;
}
/// <summary>
/// Interpolates the motion to the target.
/// </summary>
/// <param name="duration">How long the interpolation should take.</param>
/// <param name="to">The value it should interpolate.</param>
/// <param name="interpolationCurve">The curve how he interpolates. Preset values can be found under <see cref="AnimationCurves"/>. Default: <see cref="AnimationCurves.Linear"/></param>
/// <returns>A handle to control the interpolation process.</returns>
public InterpolationProcess Interpolate(TimeSpan duration, Vector4 to, BezierCurve? interpolationCurve = null)
{
InterpolationProcess process = new InterpolationProcess(this, duration, ConvertToVector4(), to, interpolationCurve.GetValueOrDefault(AnimationCurves.Linear));
process.Start();
return process;
}
/// <inheritdoc />
public override void Set(float uniform, bool triggerChanged = true)
{
W = uniform;
base.Set(uniform, triggerChanged);
}
/// <summary>
/// Sets the a own value to each component.
/// </summary>
public void Set(float x, float y, float z, float w, bool triggerChanged = true)
{
W = w;
base.Set(x, y, z, triggerChanged);
}
/// <summary>
/// Sets each component to the <see cref="Vector4" /> counter-part.
/// </summary>
public void Set(Vector4 vector, bool triggerChanged = true)
{
Set(vector.X, vector.Y, vector.Z, vector.W, triggerChanged);
}
/// <inheritdoc />
public override void SetRaw(params float[] parameters)
{
base.SetRaw(parameters);
W = parameters[3];
}
/// <inheritdoc />
public override void Add(float uniform, bool triggerChanged = true)
{
W += uniform;
base.Add(uniform, triggerChanged);
}
/// <summary>
/// Adds a <see cref="Vector4"/> to the CVector.
/// </summary>
/// <param name="vector"></param>
/// <param name="triggerChanged">If false, the event Changed doesn't gets triggered </param>
public void Add(Vector4 vector, bool triggerChanged = true)
{
Add(vector.X, vector.Y, vector.Z, vector.W, triggerChanged);
}
/// <summary>
/// Adds the values to the CVector.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
/// <param name="w"></param>
/// <param name="triggerChanged">If false, the event Changed doesn't gets triggered </param>
public void Add(float x, float y, float z, float w, bool triggerChanged = true)
{
W = w;
base.Add(x, y, z, triggerChanged);
}
/// <inheritdoc />
public override string ToString()
{
return "{" + X + "; " + Y + "; " + Z + "; " + W + "}";
}
/// <inheritdoc />
protected override float GetLengthProcess()
{
return base.GetLengthProcess() + W * W;
}
/// <inheritdoc />
protected override void NormalizationProcess(float length)
{
base.NormalizationProcess(length);
W *= length;
}
/// <inheritdoc />
protected override Vector4 ConvertToVector4()
{
return new Vector4(X, Y, Z, W);
}
/// <summary>
/// Converts a <see cref="CVector4"/> into a <see cref="Vector4"/>.
/// </summary>
public static implicit operator Vector4(CVector4 vector) => new Vector4(vector.X, vector.Y, vector.Z, vector.W);
/// <summary>
/// Converts a <see cref="Vector4"/> into a <see cref="CVector4"/>.
/// </summary>
public static implicit operator CVector4(Vector4 vector) => new CVector4(vector.X, vector.Y, vector.Z, vector.W);
}
}

View file

@ -0,0 +1,83 @@
using System;
using OpenTK;
namespace SM.Base.Types
{
/// <summary>
/// Basis for the CVector classes
/// </summary>
public abstract class CVectorBase
{
/// <summary>
/// This event triggers when a component changed.
/// </summary>
public event Action Changed;
/// <summary>
/// The length/magnitute of the vector.
/// </summary>
public float Length => GetLength();
/// <summary>
/// Gets the square of the vector length (magnitude).
/// </summary>
/// <remarks>
/// This property avoids the costly square root operation required by the Length property. This makes it more suitable
/// for comparisons.
/// </remarks>
public float LengthSquared => GetLength(true);
/// <summary>
/// Get the length of the vector.
/// </summary>
/// <param name="squared">If true, it will return the squared product.</param>
/// <returns></returns>
public float GetLength(bool squared = false)
{
float length = GetLengthProcess();
if (squared) return length;
return (float)Math.Sqrt(length);
}
/// <summary>
/// Normalizes the vector.
/// </summary>
public void Normalize()
{
float length = GetLength();
NormalizationProcess(length);
}
/// <summary>
/// Sets the values of the vector, by providing the values over an array.
/// </summary>
/// <param name="parameters"></param>
public abstract void SetRaw(params float[] parameters);
/// <summary>
/// This triggers the <see cref="Changed"/> event.
/// </summary>
protected void TriggerChanged()
{
Changed?.Invoke();
}
/// <summary>
/// Conversion from <see cref="float" /> to One-dimensional Vector.
/// </summary>
/// <returns></returns>
protected abstract float GetLengthProcess();
/// <summary>
/// Normalizes the vector.
/// </summary>
/// <param name="length"></param>
protected abstract void NormalizationProcess(float length);
/// <summary>
/// Converts the vector to a <see cref="Vector4"/>
/// </summary>
/// <returns></returns>
protected abstract Vector4 ConvertToVector4();
}
}

View file

@ -0,0 +1,60 @@
#region usings
using System;
using System.IO;
using System.Reflection;
#endregion
namespace SM.Base.Utility
{
/// <summary>
/// Contains utility functions for handling with assemblies.
/// </summary>
public class AssemblyUtility
{
/// <summary>
/// Read a file that is saved in a assembly
/// </summary>
/// <param name="ass">The assembly that contains the file</param>
/// <param name="path">The path to the file inside the assembly</param>
/// <returns></returns>
public static string ReadAssemblyFile(Assembly ass, string path)
{
return new StreamReader(GetAssemblyStream(ass, path)).ReadToEnd();
}
/// <summary>
/// Read a file that is saved in the calling assembly
/// </summary>
/// <param name="path">The path to the file inside the assembly</param>
/// <returns></returns>
public static string ReadAssemblyFile(string path)
{
return ReadAssemblyFile(Assembly.GetCallingAssembly(), path);
}
/// <summary>
/// Returns a stream of a requested resource.
/// </summary>
/// <param name="ass">The assembly the resource is in.</param>
/// <param name="path">The path to the resource.</param>
/// <returns>The stream</returns>
public static Stream GetAssemblyStream(Assembly ass, string path)
{
return ass.GetManifestResourceStream(path) ??
throw new InvalidOperationException("Assembly couldn't find resource at path '" + path + "'.");
}
/// <summary>
/// Returns a stream of a requested resource in the calling assembly.
/// </summary>
/// <param name="path">The path to the resource</param>
/// <returns>The stream</returns>
public static Stream GetAssemblyStream(string path)
{
return GetAssemblyStream(Assembly.GetCallingAssembly(), path);
}
}
}

View file

@ -0,0 +1,55 @@
namespace SM.Base.Utility
{
/// <summary>
/// A assistant to control the delta time.
/// </summary>
public class Deltatime
{
/// <summary>
/// Scaling of the delta time.
/// <para>Default: 1</para>
/// </summary>
public float Scale;
/// <summary>
/// If true, it uses <see cref="RenderDelta" />, otherwise <see cref="UpdateDelta" />.
/// <para>Default: false</para>
/// </summary>
public bool UseRender;
/// <summary>
/// The current update delta time.
/// </summary>
public static float UpdateDelta { get; internal set; }
/// <summary>
/// The current fixed update delta time.
/// </summary>
public static float FixedUpdateDelta { get; set; }
/// <summary>
/// The current render delta time.
/// </summary>
public static float RenderDelta { get; internal set; }
/// <summary>
/// The calculated delta time.
/// </summary>
public float DeltaTime => (UseRender ? RenderDelta : UpdateDelta) * Scale;
/// <summary>
/// Creates a delta time assistant.
/// </summary>
/// <param name="scale">The start scale for the delta time. Default: 1</param>
/// <param name="useRender">
/// If true, it uses <see cref="RenderDelta" />, otherwise <see cref="UpdateDelta" />. Default:
/// false
/// </param>
public Deltatime(float scale = 1, bool useRender = false)
{
UseRender = useRender;
Scale = scale;
}
}
}

View file

@ -0,0 +1,22 @@
namespace SM.Base.Utility
{
/// <summary>
///
/// </summary>
public interface IInitializable
{
/// <summary>
/// If true, its already initialized.
/// </summary>
bool IsInitialized { get; set; }
/// <summary>
/// A event when the object was activated.
/// </summary>
void Activate();
/// <summary>
/// A event, when the object was first initialized.
/// </summary>
void Initialization();
}
}

View file

@ -0,0 +1,93 @@
#region usings
using System;
using System.Collections.Generic;
#endregion
namespace SM.Base.Utility
{
/// <summary>
/// A global helper class for randomization.
/// </summary>
public static class Randomize
{
/// <summary>
/// The randomizer.
/// </summary>
public static Random Randomizer = new Random();
/// <summary>
/// Sets the seed for the randomizer.
/// </summary>
/// <param name="seed">The specified seed.</param>
public static void SetSeed(int seed)
{
Randomizer = new Random(seed);
}
/// <summary>
/// Generates a double and checks if its under the tolerance.
/// </summary>
public static bool GetBool(float tolerance)
{
return Randomizer.NextDouble() < tolerance;
}
/// <summary>
/// Generates a integer.
/// </summary>
public static int GetInt()
{
return Randomizer.Next();
}
/// <summary>
/// Generates a integer with a maximum.
/// </summary>
public static int GetInt(int max)
{
return Randomizer.Next(max);
}
/// <summary>
/// Generates a integer with a minimum and maximum
/// </summary>
public static int GetInt(int min, int max)
{
return Randomizer.Next(min, max);
}
/// <summary>
/// Generates a float between 0 and 1.
/// </summary>
public static float GetFloat()
{
return (float) Randomizer.NextDouble();
}
/// <summary>
/// Generates a float between 0 and the specified maximum.
/// </summary>
public static float GetFloat(float max)
{
return (float) Randomizer.NextDouble() * max;
}
/// <summary>
/// Generates a float between the specified minimum and the specified maximum.
/// </summary>
public static float GetFloat(float min, float max)
{
return (float) Randomizer.NextDouble() * (max - min) + min;
}
/// <summary>
/// Gets a random item from the provided list.
/// </summary>
public static TSource GetRandomItem<TSource>(this IList<TSource> list)
{
return list[GetInt(0, list.Count - 1)];
}
}
}

View file

@ -0,0 +1,87 @@
#region usings
using System;
using OpenTK;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Utility
{
/// <summary>
/// A utility class to calculate rays.
/// </summary>
public struct Ray
{
/// <summary>
/// The position of the ray.
/// </summary>
public Vector3 Position;
/// <summary>
/// The direction of the ray.
/// </summary>
public Vector3 Direction;
/// <summary>
/// Constructs a ray.
/// </summary>
/// <param name="position">The position of the ray</param>
/// <param name="direction">The direction of the ray.</param>
public Ray(Vector3 position, Vector3 direction)
{
Position = position;
Direction = direction.Normalized();
}
/// <summary>
/// Calculates a object intersection with OBB.
/// </summary>
/// <param name="box">The bounding box of the object</param>
/// <param name="modelMatrix">The model matrix of the object</param>
/// <param name="distance">Distance to the object.</param>
/// <returns>If true, it intersects with the object.</returns>
public bool ObjectIntersection(BoundingBox box, Matrix4 modelMatrix, out float distance)
{
distance = 0.0f;
float tMin = 0.0f;
float tMax = 100000.0f;
Vector3 delta = modelMatrix.Row3.Xyz - Position;
for (int i = 0; i < 3; i++)
{
Vector3 axis = new Vector3(modelMatrix[i, 0], modelMatrix[i, 1], modelMatrix[i, 2]);
float e = Vector3.Dot(axis, delta);
float f = Vector3.Dot(Direction, axis);
if (Math.Abs(f) > 0.001f)
{
float t1 = (e + box.Min[i]) / f;
float t2 = (e + box.Max[i]) / f;
if (t1 > t2)
{
float w = t1;
t1 = t2;
t2 = w;
}
if (t2 < tMax) tMax = t2;
if (t1 > tMin) tMin = t1;
if (tMax < tMin)
return false;
}
else
{
if (-e + box.Min[i] > 0.0f || -e + box.Max[i] < 0.0f)
return false;
}
}
distance = tMin;
return true;
}
}
}

View file

@ -0,0 +1,24 @@
#region usings
using System;
using OpenTK;
#endregion
namespace SM.Base.Utility
{
/// <summary>
/// Utilitys for rotations
/// </summary>
public class RotationUtility
{
/// <summary>
/// Angle towards an coordinate.
/// </summary>
/// <returns></returns>
public static float TurnTowards(Vector2 origin, Vector2 target)
{
return MathHelper.RadiansToDegrees((float) Math.Atan2(origin.X - target.X, target.Y - origin.Y));
}
}
}

View file

@ -0,0 +1,38 @@
#region usings
using System;
#endregion
namespace SM.Base.Utility
{
/// <summary>
/// Utility-Functions that are too small for a own class.
/// </summary>
public class Util
{
/// <summary>
/// Activates a <see cref="IInitializable"/>
/// </summary>
/// <param name="obj"></param>
public static void Activate(IInitializable obj)
{
if (!obj.IsInitialized)
{
obj.Initialization();
obj.IsInitialized = true;
}
obj.Activate();
}
/// <summary>
/// Calls a garbage collector.
/// </summary>
public static void CallGarbageCollector()
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}

View file

@ -0,0 +1,91 @@
#region usings
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Drawing;
using SM.Base.Scene;
using SM.Base.Shaders;
using SM.OGL.Mesh;
#endregion
namespace SM.Base.Window
{
/// <summary>
/// The draw context contains (more or less) important information for drawing a object.
/// </summary>
public struct DrawContext
{
/// <summary>
/// The window, the context came origionally.
/// </summary>
public IGenericWindow Window { get; internal set; }
/// <summary>
/// The scene, the context was send to.
/// </summary>
public GenericScene Scene { get; internal set; }
/// <summary>
/// The render pipeline, the context is using.
/// </summary>
public RenderPipeline RenderPipeline { get; internal set; }
/// <summary>
/// The last object it came though.
/// <para>Debugging pourpose.</para>
/// </summary>
public object LastObject { get; internal set; }
/// <summary>
/// The camera the context is using.
/// </summary>
public GenericCamera UseCamera { get; internal set; }
/// <summary>
/// The world matrix.
/// </summary>
public Matrix4 World => UseCamera.World;
/// <summary>
/// The view matrix.
/// </summary>
public Matrix4 View => UseCamera.View;
/// <summary>
/// The mesh, that is rendered.
/// </summary>
public GenericMesh Mesh { get; set; }
/// <summary>
/// If set, it will force the mesh to render with that primitive type.
/// </summary>
public PrimitiveType? ForcedType { get; set; }
/// <summary>
/// The material the mesh is going to use.
/// </summary>
public Material Material { get; set; }
/// <summary>
/// The shader the mesh is going to use.
/// </summary>
public MaterialShader Shader => Material.CustomShader ?? RenderPipeline.DefaultShader;
/// <summary>
/// The master model matrix.
/// </summary>
public Matrix4 ModelMatrix;
/// <summary>
/// The master texture matrix.
/// </summary>
public Matrix3 TextureMatrix;
/// <summary>
/// Instances
/// </summary>
public IList<Instance> Instances;
/// <summary>
/// This sets the camera of the context.
/// </summary>
/// <param name="camera"></param>
public void SetCamera(GenericCamera camera)
{
UseCamera = camera;
camera.CalculateViewMatrix(Window);
}
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SM.Base.Window.Contexts
{
/// <summary>
/// A context for the fixed update.
/// </summary>
public struct FixedUpdateContext
{
}
}

View file

@ -0,0 +1,29 @@
#region usings
using SM.Base.Scene;
#endregion
namespace SM.Base.Window
{
/// <summary>
/// A context that gets send when a window want to update the scene.
/// </summary>
public struct UpdateContext
{
/// <summary>
/// The window what triggered the updated.
/// </summary>
public IGenericWindow Window;
/// <summary>
/// A current update delta time. Equivalent to <see cref="SMRenderer.DefaultDeltatime"/>.
/// </summary>
public float Deltatime => SMRenderer.DefaultDeltatime.DeltaTime;
/// <summary>
/// The scene that gets updated.
/// </summary>
public GenericScene Scene;
}
}

View file

@ -0,0 +1,349 @@
#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
{
/// <summary>
/// This provides the main entry, by executing the window.
/// </summary>
public class GLWindow : GameWindow, IGenericWindow
{
private Vector2 _flagWindowPos;
private Vector2 _flagWindowSize;
private Thread _fixedUpdateThread;
private WindowFlags _windowFlags;
private DisplayResolution _normalResolution;
private DisplayResolution _fullscreenResolution;
/// <inheritdoc />
public bool Loading { get; private set; } = true;
/// <inheritdoc />
public float AspectRatio { get; set; }
/// <inheritdoc />
public GenericCamera ViewportCamera { get; set; }
/// <inheritdoc />
public bool ForceViewportCamera { get; set; }
/// <inheritdoc />
public bool DrawWhileUnfocused { get; set; } = true;
/// <inheritdoc />
public bool UpdateWhileUnfocused { get; set; } = false;
/// <inheritdoc />
public Vector2 WindowSize { get; set; }
/// <inheritdoc />
public ISetup AppliedSetup { get; private set; }
/// <inheritdoc />
public new event Action<IGenericWindow> Resize;
/// <inheritdoc />
public new event Action<IGenericWindow> Load;
/// <inheritdoc />
public GenericScene CurrentScene { get; private set; }
/// <inheritdoc />
public RenderPipeline CurrentRenderPipeline { get; private set; }
/// <summary>
/// Gets/Sets the current window flag.
/// </summary>
public WindowFlags WindowFlags
{
get => _windowFlags;
set
{
if (_windowFlags != value)
{
WindowFlags oldV = _windowFlags;
_windowFlags = value;
ChangeWindowFlag(value, oldV);
}
}
}
/// <summary>
/// Loads the window with default values.
/// <para>Width: 1280px; Height: 720px; Title: Generic OpenGL Title; WindowFlag: Window</para>
/// </summary>
public GLWindow() : this(1280, 720, "Generic OpenGL Title", WindowFlags.Window)
{
}
/// <summary>
/// Loads the window with custom values.
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="title"></param>
/// <param name="flags"></param>
/// <param name="vSync"></param>
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);
}
/// <summary>
/// A event that gets executed when the window is done loading.
/// </summary>
public event Action<IGenericWindow> Loaded;
/// <inheritdoc />
protected override void OnLoad(EventArgs e)
{
WindowCode.Load(this);
SMRenderer.CurrentWindow = this;
base.OnLoad(e);
}
/// <inheritdoc />
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
WindowCode.Resize(this);
if (Loading)
{
Loading = false;
Loaded?.Invoke(this);
AppliedSetup?.Loaded(this);
OnLoaded();
}
}
/// <summary>
/// This gets called, when the window completed with all initilization steps.
/// </summary>
protected virtual void OnLoaded()
{
Icon ??= new Icon(AssemblyUtility.GetAssemblyStream("SM.Base.Window.winIcon.ico"));
}
/// <inheritdoc />
protected override void OnUpdateFrame(FrameEventArgs e)
{
if (!Focused && !UpdateWhileUnfocused) return;
base.OnUpdateFrame(e);
WindowCode.Update(this, (float) e.Time);
}
/// <inheritdoc />
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
WindowCode.Render(this, (float) e.Time);
SwapBuffers();
GLDebugging.CheckGLErrors();
}
/// <inheritdoc />
protected override void OnMouseMove(MouseMoveEventArgs e)
{
base.OnMouseMove(e);
Mouse.MouseMoveEvent(e, this);
}
/// <inheritdoc />
public void TriggerLoad()
{
Load?.Invoke(this);
}
/// <inheritdoc />
public void TriggerResize()
{
Resize?.Invoke(this);
}
/// <inheritdoc />
public void Update(UpdateContext context)
{
}
/// <inheritdoc />
public void ApplySetup(ISetup setup)
{
AppliedSetup = setup;
setup.Applied(this);
}
/// <inheritdoc />
public void SetScene(GenericScene scene)
{
if (Loading)
{
Loaded += window => SetScene(scene);
return;
}
WindowCode.PrepareScene(this, scene);
CurrentScene = scene;
}
/// <inheritdoc />
public void SetRenderPipeline(RenderPipeline renderPipeline)
{
if (Loading)
{
Loaded += window => SetRenderPipeline(renderPipeline);
return;
}
WindowCode.PreparePipeline(this, renderPipeline);
CurrentRenderPipeline = renderPipeline;
}
/// <summary>
/// Changes the resolution in fullscreen mode.
/// <para>Can be executed before changing to fullscreen, but with no effect until changed.</para>
/// </summary>
/// <param name="resolution">The resolution you get from <see cref="DisplayDevice.AvailableResolutions"/> or <see cref="DisplayDevice.SelectResolution"/></param>
public void ChangeFullscreenResolution(DisplayResolution resolution)
{
_fullscreenResolution = resolution;
if (_windowFlags == WindowFlags.ExclusiveFullscreen) ApplyFullscreenResolution();
}
/// <summary>
/// Starts the fixed update loop.
/// <para>Need to get executed before <see cref="IFixedScriptable"/> can be used.</para>
/// </summary>
/// <param name="updatesPerSecond"></param>
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;
}
}
}

Some files were not shown because too many files have changed in this diff Show more