04.10.2020

+ render pipeline system for more control about the renderering.
+ Log system
+ Framebuffer system

~ Default shader was moved to pipelines.
This commit is contained in:
Michel Fedde 2020-10-04 16:31:48 +02:00
parent 97e638d9d9
commit 820d6ce700
34 changed files with 660 additions and 106 deletions

View file

@ -1,6 +1,7 @@
using SM.Base.Objects;
using SM.Base.Objects.Static;
using SM.Base.Scene;
using SM.Base.Text;
using SM.OGL.Mesh;
using SM.Utility;
@ -11,15 +12,16 @@ namespace SM.Base
/// </summary>
public class Defaults
{
/// <summary>
/// The default shader.
/// </summary>
public static IShader DefaultShader;
/// <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>

View file

@ -25,20 +25,21 @@ namespace SM.Base.Scene
}
/// <inheritdoc />
public virtual void Draw(DrawContext context)
public void Draw(DrawContext context)
{
context.Material = _material;
context.Mesh = _mesh;
DrawContext(ref context);
}
/// <summary>
/// Applies the current settings to the context.
/// Draws the context, that was given to them.
/// </summary>
/// <param name="context"></param>
protected void ApplyContext(ref DrawContext context)
protected virtual void DrawContext(ref DrawContext context)
{
_material.Shader ??= Defaults.DefaultShader;
context.Material = _material;
context.Mesh = _mesh;
}
}
/// <summary>

View file

@ -18,9 +18,8 @@ namespace SM.Base.Scene
public Color4 Tint = Color4.White;
/// <summary>
/// A shader, that is used to draw this material.
/// <para>Default: Defaults.DefaultShaders </para>
/// A custom shader, that is used to draw this material.
/// </summary>
public IShader Shader = Defaults.DefaultShader;
public IShader CustomShader;
}
}

236
SMCode/SM.Base/Log.cs Normal file
View file

@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL4;
using SM.OGL;
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 = false;
/// <summary>
/// Presets for the log targets.
/// </summary>
public static Dictionary<LogTarget, string> Preset = new Dictionary<LogTarget, string>()
{
{LogTarget.Console, "[%type%] %msg%"},
{LogTarget.Debugger, "[%type%] %msg%"},
{LogTarget.File, "<%date%, %time%> [%type%] %msg%"}
};
private static readonly Dictionary<LogType, ConsoleColor> Colors = new Dictionary<LogType, ConsoleColor>()
{
{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 != "")
{
DateTime creation = File.GetLastWriteTime(path);
try
{
using ZipArchive 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, $"Activated new log file. ['{path}']");
}
static void Init()
{
AppDomain.CurrentDomain.UnhandledException += ExceptionHandler;
AppDomain.CurrentDomain.DomainUnload += (sender, args) =>
{
_logStream.WriteLine("Unload application");
_logStream.Close();
};
GLDebugging.DebugAction = GLDebugAction;
GLDebugging.GlErrorAction = code =>
{
Write(LogType.Warning, $"A '{code}' GL error occured.");
};
_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(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<T>(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<T>(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)
{
ConsoleColor before = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(value);
Console.ForegroundColor = before;
}
static string ProcessPreset(LogTarget target, string type, string msg)
{
string preset = Preset[target];
DateTime now = DateTime.Now;
return preset.Replace("%date%", now.ToShortDateString())
.Replace("%time%", now.ToShortTimeString())
.Replace("%type%", type)
.Replace("%msg%", msg);
}
}
}

View file

@ -40,6 +40,9 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -54,6 +57,7 @@
<Compile Include="Drawing\GenericTransformation.cs" />
<Compile Include="Drawing\Instance.cs" />
<Compile Include="Drawing\IShader.cs" />
<Compile Include="Log.cs" />
<Compile Include="Objects\Mesh.cs" />
<Compile Include="Scene\IShowCollection.cs" />
<Compile Include="Scene\IShowItem.cs" />
@ -82,6 +86,7 @@
<Compile Include="Scene\GenericCamera.cs" />
<Compile Include="Scene\GenericScene.cs" />
<Compile Include="Objects\Static\Plate.cs" />
<Compile Include="Window\RenderPipeline.cs" />
</ItemGroup>
<ItemGroup>
<None Include="OpenTK.dll.config" />

View file

@ -54,7 +54,7 @@ namespace SM.Base.Scene
public abstract bool Orthographic { get; }
/// <summary>
/// This will calculate the world.
/// <para>This is called on <see cref="GenericWindow{TScene,TItem,TCamera}.ViewportCamera"/> to calculate the world.</para>
/// <para>This is called on <see cref="GenericWindow{TScene,TCamera}.ViewportCamera"/> to calculate the world.</para>
/// </summary>
/// <param name="world">The world scale</param>
/// <param name="aspect">The aspect ratio from the window.</param>

View file

@ -7,23 +7,24 @@ namespace SM.Base.Scene
/// Contains a list of show items.
/// </summary>
/// <typeparam name="TItem">The type of show items.</typeparam>
public abstract class GenericItemCollection<TItem> : IShowItem, IShowCollection<TItem>
public abstract class GenericItemCollection<TItem> : List<TItem>, IShowItem, IShowCollection<TItem>
where TItem : IShowItem
{
/// <inheritdoc />
public List<TItem> Objects { get; } = new List<TItem>();
public List<TItem> Objects => this;
/// <inheritdoc />
public void Update(UpdateContext context)
{
throw new System.NotImplementedException();
for(int i = 0; i < Objects.Count; i++)
this[i].Update(context);
}
/// <inheritdoc cref="IShowCollection{TItem}.Draw" />
public virtual void Draw(DrawContext context)
{
for (int i = 0; i < Objects.Count; i++)
Objects[i].Draw(context);
this[i].Draw(context);
}
}

View file

@ -4,13 +4,48 @@ using SM.Base.Contexts;
namespace SM.Base.Scene
{
/// <summary>
/// A generic scene, that imports functions for scene control.
/// </summary>
public abstract class GenericScene
{
/// <summary>
/// This contains the background.
/// </summary>
protected IBackgroundItem _background;
/// <summary>
/// Draws this scene.
/// </summary>
public virtual void Draw(DrawContext context)
{
}
/// <summary>
/// Called, when the user activates the scene.
/// </summary>
internal void Activate()
{
OnActivating();
}
/// <summary>
/// Called, when the user activates the scene.
/// </summary>
protected virtual void OnActivating()
{ }
}
/// <summary>
/// A generic scene that imports different functions.
/// </summary>
/// <typeparam name="TCamera">The type of cameras.</typeparam>
/// <typeparam name="TItem">The type of show items.</typeparam>
public abstract class GenericScene<TCamera, TItem> : GenericItemCollection<TItem>
/// <typeparam name="TCollection">The type for collections</typeparam>
public abstract class GenericScene<TCamera, TCollection, TItem> : GenericScene
where TCamera : GenericCamera, new()
where TCollection : GenericItemCollection<TItem>, new()
where TItem : IShowItem
{
/// <summary>
@ -28,13 +63,13 @@ namespace SM.Base.Scene
public TCamera HUDCamera { get; set; } = new TCamera();
/// <summary>
/// This contains the background.
/// Objects inside the scene.
/// </summary>
protected IBackgroundItem _background;
public TCollection Objects { get; set; } = new TCollection();
/// <summary>
/// This defines the HUD objects.
/// </summary>
public List<TItem> HUD { get; } = new List<TItem>();
public TCollection HUD { get; } = new TCollection();
/// <summary>
/// A collection for cameras to switch easier to different cameras.
/// </summary>
@ -49,25 +84,10 @@ namespace SM.Base.Scene
backgroundDrawContext.View = BackgroundCamera.CalculateViewMatrix();
_background?.Draw(backgroundDrawContext);
base.Draw(context);
Objects.Draw(context);
context.View = HUDCamera.CalculateViewMatrix();
for (int i = 0; i < HUD.Count; i++)
HUD[i].Draw(context);
HUD.Draw(context);
}
/// <summary>
/// Called, when the user activates the scene.
/// </summary>
internal void Activate()
{
OnActivating();
}
/// <summary>
/// Called, when the user activates the scene.
/// </summary>
protected virtual void OnActivating()
{ }
}
}

View file

@ -127,7 +127,7 @@ namespace SM.Base.Text
}
/// <inheritdoc />
protected override void Compile()
public override void Compile()
{
RegenerateTexture();
base.Compile();

View file

@ -75,9 +75,8 @@ namespace SM.Base.Text
/// <inheritdoc />
public override void Draw(DrawContext context)
protected override void DrawContext(ref DrawContext context)
{
base.Draw(context);
if (_instances == null) GenerateMatrixes();
}

View file

@ -46,7 +46,7 @@ namespace SM.Base.Textures
/// <inheritdoc />
protected override void Compile()
public override void Compile()
{
base.Compile();
@ -54,7 +54,7 @@ namespace SM.Base.Textures
}
/// <inheritdoc />
protected override void Dispose()
public override void Dispose()
{
base.Dispose();

View file

@ -38,9 +38,20 @@ namespace SM.Base.Contexts
/// </summary>
public Material Material;
/// <summary>
/// Contains the currently used render pipeline.
/// </summary>
public RenderPipeline ActivePipeline;
/// <summary>
/// The current world scale.
/// </summary>
public Vector2 WorldScale;
/// <summary>
/// Returns the appropriate shader.
/// <para>Returns the material shader, if available, otherwise it will take the default shader from the render pipeline.</para>
/// </summary>
public IShader Shader => Material.CustomShader ?? ActivePipeline._defaultShader;
}
}

View file

@ -41,14 +41,14 @@ namespace SM.Base
{
GLSystem.INIT_SYSTEM();
Console.Write("----------------------\n" +
"--- OpenGL Loading ---\n" +
"----------------------------------\n" +
$"--- {"DeviceVersion",14}: {GLSystem.DeviceVersion,-10} ---\n" +
$"--- {"ForcedVersion",14}: {GLSystem.ForcedVersion,-10} ---\n" +
$"--- {"ShadingVersion",14}: {GLSystem.ShadingVersion,-10} ---\n" +
$"--- {"Debugging",14}: {GLSystem.Debugging,-10} ---\n" +
$"----------------------------------\n");
Log.Write("#", ConsoleColor.Cyan, "----------------------",
"--- OpenGL Loading ---",
"----------------------------------",
$"--- {"DeviceVersion",14}: {GLSystem.DeviceVersion,-10} ---",
$"--- {"ForcedVersion",14}: {GLSystem.ForcedVersion,-10} ---",
$"--- {"ShadingVersion",14}: {GLSystem.ShadingVersion,-10} ---",
$"--- {"Debugging",14}: {GLSystem.Debugging,-10} ---",
$"----------------------------------");
base.OnLoad(e);
_loading = true;
@ -123,11 +123,9 @@ namespace SM.Base
/// The base window.
/// </summary>
/// <typeparam name="TScene">The scene type</typeparam>
/// <typeparam name="TItem">The base item type</typeparam>
/// <typeparam name="TCamera">The camera type</typeparam>
public abstract class GenericWindow<TScene, TItem, TCamera> : GenericWindow
where TScene : GenericScene<TCamera, TItem>, new()
where TItem : IShowItem
public abstract class GenericWindow<TScene, TCamera> : GenericWindow
where TScene : GenericScene, new()
where TCamera : GenericCamera, new()
{
/// <summary>
@ -144,6 +142,11 @@ namespace SM.Base
/// </summary>
public TScene CurrentScene { get; private set; }
/// <summary>
/// Controls how a scene is rendered.
/// </summary>
public RenderPipeline<TScene> RenderPipeline { get; private set; }
/// <inheritdoc />
protected GenericWindow()
{
@ -153,6 +156,7 @@ namespace SM.Base
/// <inheritdoc />
protected override void OnRenderFrame(FrameEventArgs e)
{
Deltatime.RenderDelta = (float)e.Time;
DrawContext drawContext = new DrawContext()
{
World = ViewportCamera.World,
@ -165,11 +169,11 @@ namespace SM.Base
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
CurrentScene.Draw(drawContext);
RenderPipeline.Render(ref drawContext, CurrentScene);
SwapBuffers();
GLDebugging.CheckGLErrors();
}
/// <inheritdoc />
@ -178,6 +182,7 @@ namespace SM.Base
base.OnResize(e);
ViewportCamera.RecalculateWorld(_worldScale, Aspect);
RenderPipeline.Resize();
}
/// <summary>
@ -189,5 +194,15 @@ namespace SM.Base
CurrentScene = scene;
scene.Activate();
}
/// <summary>
/// Defines the render pipeline.
/// </summary>
/// <param name="pipeline"></param>
public void SetRenderPipeline(RenderPipeline<TScene> pipeline)
{
RenderPipeline = pipeline;
pipeline.Activate(this);
}
}
}

View file

@ -0,0 +1,74 @@
using System.Collections.Generic;
using SM.Base.Contexts;
using SM.Base.Scene;
using SM.OGL.Framebuffer;
namespace SM.Base
{
/// <summary>
/// Definition of specific render options.
/// </summary>
public abstract class RenderPipeline
{
/// <summary>
/// The framebuffers, that are used in this Pipeline.
/// </summary>
protected virtual List<Framebuffer> _framebuffers { get; }
/// <summary>
/// The default shader for the pipeline.
/// </summary>
protected internal virtual IShader _defaultShader { get; }
/// <summary>
/// Occurs, when the window is loading.
/// </summary>
protected internal virtual void Load()
{
foreach (Framebuffer framebuffer in _framebuffers)
framebuffer.Compile();
}
/// <summary>
/// Occurs, when the window is resizing.
/// </summary>
protected internal virtual void Resize()
{ }
/// <summary>
/// Occurs, when the pipeline was connected to a window.
/// </summary>
protected internal virtual void Activate(GenericWindow window)
{
}
/// <summary>
/// Occurs, when the window is unloading.
/// </summary>
protected internal virtual void Unload()
{
foreach (Framebuffer framebuffer in _framebuffers)
{
framebuffer.Dispose();
}
}
}
/// <summary>
/// Represents a render pipeline.
/// </summary>
/// <typeparam name="TScene">The scene type</typeparam>
public abstract class RenderPipeline<TScene> : RenderPipeline
where TScene : GenericScene
{
/// <summary>
/// The system to render stuff.
/// </summary>
protected internal virtual void Render(ref DrawContext context, TScene scene)
{
context.ActivePipeline = this;
}
}
}