WickedEngine.Net

About

A C# wrapper around Turanszkijs 'Wicked Engine'. This is currently extremly WIP.

Status

  • Basic Wrapper (hand authored).
    • Initialize the native engine and provide a HWND to host.
    • Camera manipulation.
    • Weather manipulation.
    • GLTF model loading.
    • Entity wrapper.
    • Transform manipulation.
  • Generic WPF Control.
  • Example WPF Application.

Screenshots

Flight Helmet opened in the sample application as of the 9th of May, 2023

Open 3D Viewer

About

An open source 3D model viewer inspired by Microsofts '3D Viewer'.

Media

Flight Helmet opened in the tool as of the 15th of January, 2023

Sponza opened in the tool as of the 15th of January, 2023

BeautifulGame opened in the tool as of the 15th of January, 2023

Features

  • Basic GLTF model format support.
  • Basic 3D renderer.
  • Support or multiple shading modes.

Roadmap

  • A full PBR render.
  • IBL supported rendering.
  • Skybox support.
  • Animation support.
  • Support for more than just GLTF models.

Thanks

Object icons created by Freepik - Flaticon

ModernUI.Xceed.Toolkit

About

ModernUI.Xceed.Toolkit contains all style resources to seamlessly integrate Xceeds WPF extended toolkit into the ModernUI framework.

GitHub Nuget

Controls

The following controls have had style resources fully created for them:

Up/Down Controls

  • ByteUpDown
  • CalculatorUpDown
  • DateTimeUpDown
  • DecimalUpDown
  • DoubleUpDown
  • IntegerUpDown
  • LongUpDown
  • ShortUpDown
  • SingleUpDown
  • TimeSpanUpDown

Standard Controls

  • AutoSelectTextBox
  • Calculator
  • CollectionControl
  • DropDownButton
  • DateTimePicker
  • MaskedTextBox
  • MultiLineTextEditor
  • NumericUpDown
  • PrimitiveTypeCollEditor
  • PropertyGrid
  • RangeSlider
  • RichTextBox
  • RichTextBoxFormatBar
  • SplitButton
  • TimePicker
  • ValueRangeTextBox
  • WatermarkTextBox
  • WatermarkComboBox

Color Controls

  • ColorCanvas
  • ColorPicker

Check Controls

  • CheckComboBox
  • CheckListBox

Misc Controls

  • ButtonSpinner
  • ChromeButton

Aditional Libraries

  • AvalonDock

The following controls will be implemented in separate libraries:

  • DataGrid

Screenshots

ModernUI.Xceed.Toolkit (Dark) - Calculator

ModernUI.Xceed.Toolkit (Dark) - CheckList

ModernUI.Xceed.Toolkit (Dark) - Collection Control

ModernUI.Xceed.Toolkit (Dark) - Color Controls

ModernUI.Xceed.Toolkit (Dark) - Property Grid

ModernUI.Xceed.Toolkit (Dark) - RichTextBox

ModernUI.Xceed.Toolkit (Dark) - Standard Controls

ModernUI.Xceed.Toolkit (Dark) - Up/Down Controls

AsyncProcess

About

This library is a very simple wrapper around System.Diagnostics.Process, allowing for async/await operations. Rather than creating and starting a Process, you instead create an AsyncProcess and await on the running of said process.

Currently the library does not support all operations possible with System.Diagnostics.Process, but it does allow for the following:

  • Running a process and async awaiting for the process to end.
  • Capturing of standard output streams.
  • Capturing of standard error streams.
  • Setting of the CreateNoWindow property.
  • Setting of launch arguments
  • Setting of the current working directory.
  • Cancelling of async processes.

All run methods for the AsyncProcess return an AsyncProcessResult. This result contains the state of the result (i.e. did the process complete or did an error occur). If the process completed successfully, the result will contain the exit code of the process. If the process failed, an Exception property will be set to the exception which occurred whilst running the process.

When constructing an AsyncProcess object you may pass an option cancellation token which will be respected by the async process. However, the process may not exit immediately. When a cancellation token is set the Kill method will be executed on the internal System.Diagnostics.Process and we will wait until the processes HasExited property returns true.

GitHub Nuget

Example

Running a process and using the exit code of the process

using (var process = new AsyncProcess(new AsyncProcessStartInfo("ping.exe", "www.github.com")))
{
    var result = await process.Run();
    Console.WriteLine($"The pinging of github resulted in the exit code: {result.ExitCode}");
}

Running a process and handling an error

using (var process = new AsyncProcess(new AsyncProcessStartInfo("missing-file.exe")))
{
    var result = await process.Run();
    if (result.Exception != null)
    {
    	throw new Exception($"Failed to run 'missing-file.exe', result: ${result.CompletionState}",
							result.Exception);
    }
}

Capturing output from a process using callbacks

var startInfo = new AsyncProcessStartInfo("ping.exe", "www.github.com")
{
    OnStandardOutputReceived = message =>
    {
        Console.WriteLine($"Info: {message}");
    },
    OnStandardErrorReceived = message =>
    {
        Console.WriteLine($"Error: {message}");
    }
};

using (var process = new AsyncProcess(startInfo))
{
    var result = await process.Run();
    Console.WriteLine($"The pinging of github resulted in the exit code: {result.ExitCode}");
}

Capturing output from a process storing it in the result

var startInfo = new AsyncProcessStartInfo("ping.exe", "www.github.com")
{
    CaptureOutputToProcessResult = ProcessOutputCaptureMode.Both
};

using (var process = new AsyncProcess(startInfo))
{
    var result = await process.Run();
    Console.WriteLine($"The pinging of github resulted in the exit code: {result.ExitCode}");
	Console.WriteLine($"Standard Output: {result.StandardOutput}");	
	Console.WriteLine($"Standard Error: {result.StandardError}");	
}

Helper Methods

There are a few helper methods which allow for single line usage of the AsyncProcess class

await AsyncProcess.Run("notepad.exe")
/// <summary>
/// Create a new process, executing the specified file.
/// </summary>
/// <param name="fileName">The file to use when launching the process.</param>
/// <returns>An awaitable task containing the result of the process run.</returns>
public static async Task<AsyncProcessResult> Run(string fileName)
await AsyncProcess.Run("notepad.exe", @"C:\Windows\System32\drivers\etc\hosts")
/// <summary>
/// Create a new process, executing the specified file with the provided launch arguments.
/// </summary>
/// <param name="fileName">The file to use when launching the process.</param>
/// <param name="arguments">The arguments to use when launching the process.</param>
/// <returns>An awaitable task containing the result of the process run.</returns>
public static async Task<AsyncProcessResult> Run(string fileName, string arguments)
await AsyncProcess.Run(new AsyncProcessStartInfo("notepad.exe"))
/// <summary>
/// Create a new process, executing using the provided start info.
/// </summary>
/// <param name="startInfo">The information used when starting the new process.</param>
/// <returns>An awaitable task containing the result of the process run.</returns>
public static async Task<AsyncProcessResult> Run(AsyncProcessStartInfo startInfo)

Cake.Unity3D

About

Unity3D build support for Cake (https://github.com/cake-build/cake).

GitHub Nuget

Methods

/// <summary>
/// Build a provided Unity3D project with the specified build options.
/// </summary>
/// <param name="context">The active cake context.</param>
/// <param name="projectFolder">The absolute path to the Unity3D project to build.</param>
/// <param name="options">The build options to use when building the project.</param>
public static void BuildUnity3DProject(this ICakeContext context, FilePath projectFolder, Unity3DBuildOptions options)
/// <summary>
/// Test a provided Unity3D project with the specified test options.
/// </summary>
/// <param name="context">The active cake context.</param>
/// <param name="projectFolder">The absolute path to the Unity3D project to test.</param>
/// <param name="options">The test options to use when testing the project.</param>
public static void TestUnity3DProject(this ICakeContext context, FilePath projectFolder, Unity3DTestOptions options)
/// <summary>
/// Locate all installed version of Unity3D.
/// Warning: This currently only works for Windows and has only been tested on Windows 10.
/// </summary>
/// <param name="context">The active cake context.</param>
/// <returns>A dictionary containing 'key' Unity version, 'value' absolute install path</returns>
public static Dictionary<string, string> GetAllUnityInstalls(this ICakeContext context)
/// <summary>
/// Try and get the absolute install path for a specific Unity3D version.
/// </summary>
/// <param name="context">The active cake context.</param>
/// <param name="version">The version to try and locate.</param>
/// <param name="installPath">If found the absolute install path will be written to this out variable</param>
/// <returns>True if the editor version was found, false otherwise.</returns>
public static bool TryGetUnityInstall(this ICakeContext context, string version, out string installPath)
/// <summary>
/// Try and get the version of Unity3D a specified project uses.
/// </summary>
/// <param name="context">The active cake context.</param>
/// <param name="projectPath">The absolute path to the Unity3D project we want to get the Unity3D version for.</param>
/// <param name="unityVersion">If found the name of the Unity3D version used for the project.</param>
/// <returns>True if the editor version was found, false otherwise.</returns>
public static bool TryGetUnityVersionForProject(this ICakeContext context, string projectPath, out string unityVersion)

Example

Building a Project

#addin nuget:?package=Cake.Unity3D

var target = Argument("target", "Build");

Task("Build")
  .Does(() =>
{
	// Presuming the build.cake file is within the Unity3D project folder.
	var projectPath = System.IO.Path.GetFullPath("./");
	
	// The location we want the build application to go
	var outputPath = System.IO.Path.Combine(projectPath, "_build", "x64", "example.exe");
	
	// Get the version of Unity used by the Unity project
	string unityEditorVersion;
	if (!TryGetUnityVersionForProject(projectPath, out unityEditorVersion))
	{
		Error($"Failed to find Unity version for the project '{projectPath}'");
		return;
	}
	
	// Get the absolute path to the Unity3D editor.
	string unityEditorLocation;
	if (!TryGetUnityInstall(unityEditorVersion, out unityEditorLocation)) 
	{
		Error($"Failed to find '{unityEditorVersion}' install location");
		return;
	}
	
	// Create our build options.
	var options = new Unity3DBuildOptions()
	{
		Platform = Unity3DBuildPlatform.StandaloneWindows64,
		OutputPath = outputPath,
		UnityEditorLocation = unityEditorLocation,
		ForceScriptInstall = true,
		BuildVersion = "1.0.0"
	};
	
	// Perform the Unity3d build.
	BuildUnity3DProject(projectPath, options);
});

RunTarget(target);

Testing a Project

#addin nuget:?package=Cake.Unity3D

var target = Argument("target", "Test");

Task("Test")
  .Does(() =>
{
	// Presuming the build.cake file is within the Unity3D project folder.
	var projectPath = System.IO.Path.GetFullPath("./");
	
	// The location we want the test results to be output to (if this isn't specified the file will be output to 'test_results.xml' in the root Unity Project folder)
	var testResultsOutputPath = System.IO.Path.Combine(projectPath, "_test_results.xml");
	
	// Get the version of Unity used by the Unity project
	string unityEditorVersion;
	if (!TryGetUnityVersionForProject(projectPath, out unityEditorVersion))
	{
		Error($"Failed to find Unity version for the project '{projectPath}'");
		return;
	}
	
	// Get the absolute path to the Unity3D editor.
	string unityEditorLocation;
	if (!TryGetUnityInstall(unityEditorVersion, out unityEditorLocation)) 
	{
		Error($"Failed to find '{unityEditorVersion}' install location");
		return;
	}
	
	// Create our test options.
	var options = new Unity3DTestOptions()
	{
		TestMode = Unity3DTestMode.EditMode,
		TestResultOutputPath = testResultsOutputPath,
		UnityEditorLocation = unityEditorLocation
	};
	
	// Perform the Unity3d test.
	TestUnity3DProject(projectPath, options);
});

RunTarget(target);

Supported Platforms

Executing Platforms

Currently the addon only works on Windows and has only been tested on Windows 10. This is due to how the 'TryGetUnityInstall' works. What it does is look at all shortcuts in your Windows start menu and locate the Unity3D installs from that. In theory, if you just set the 'UnityEditorLocation' build option to the absolute path to your Unity3D install on Mac or Linux, this should also work.

Build Platforms

  • StandaloneWindows64
  • StandaloneWindows
  • WebGL

In theory, all platforms that the executing platform supports should work. However, I haven't test any apart from the above list.

LZ4.Sharp

About

A C# wrapper for LZ4, supporting multiple compression modes and decompression of data.

GitHub Nuget

Methods

/// <summary>
/// Compress a given array of bytes, storing the result within the 'out' variable 'compressedData'.
/// The compression is completed respected the settings provided.
/// </summary>
/// <param name="data">The data you want to compress using LZ4.</param>
/// <param name="compressedData">The compressed representation of the data, null if the result is not 'Success'.</param>
/// <param name="settings">The settings to use when compressing the provided data.</param>
/// <returns>An LZ4Result stating the result of the compression.</returns>
public static LZ4Result CompressBytes(byte[] data, out byte[] compressedData, LZ4CompressionSettings settings)
/// <summary>
/// Compress a string, storing the result within the 'out' variable 'compressedData'.
/// The compression is completed respected the settings provided.
/// </summary>
/// <param name="data">The string you want to compress using LZ4.</param>
/// <param name="compressedData">The compressed representation of the data, null if the result is not 'Success'.</param>
/// <param name="settings">The settings to use when compressing the provided data.</param>
/// <returns>An LZ4Result stating the result of the compression.</returns>
public static LZ4Result CompressString(string data, out byte[] compressedData, LZ4CompressionSettings settings)
/// <summary>
/// Compress a given stream, storing the result within the 'out' variable 'compressedData'.
/// The compression is completed respected the settings provided.
/// </summary>
/// <param name="stream">The stream you want to compress using LZ4.</param>
/// <param name="compressedData">The compressed representation of the data, null if the result is not 'Success'.</param>
/// <param name="settings">The settings to use when compressing the provided data.</param>
/// <returns>An LZ4Result stating the result of the compression.</returns>
public static LZ4Result CompressStream(Stream stream, out byte[] compressedData, LZ4CompressionSettings settings)
/// <summary>
/// Decompress an array of compressed data, storing the result within the 'out' variable 'data'.
/// </summary>
/// <param name="compressedData">The source compressed data to be decompressed.</param>
/// <param name="uncompressedDataSize">The expected size of the decompressed data.</param>
/// <param name="data">[out] If decompression is successful the decompressed data, else null.</param>
/// <returns>An LZ4Result stating the result of the decompression.</returns>
public static LZ4Result DecompressBytes(byte[] compressedData, int uncompressedDataSize, out byte[] data)
/// <summary>
/// Decompress a stream of compressed data, storing the result within the 'out' variable 'data'.
/// </summary>
/// <param name="compressedData">The source compressed data to be decompressed.</param>
/// <param name="uncompressedDataSize">The expected size of the decompressed data.</param>
/// <param name="data">[out] If decompression is successful the decompressed data, else null.</param>
/// <returns>An LZ4Result stating the result of the decompression.</returns>

Example

const int randomDataSize = 1024 * 512;
var dataToCompress = new byte[randomDataSize];

// Generate some random data for our data source
var random = new Random();
for (var offset = 0; offset < randomDataSize; ++offset)
{
	dataToCompress[offset] = (byte)random.Next('A', 'E');
}

foreach (var compressionSetting in new[]
	{
		LZ4CompressionSettings.Fast,
		LZ4CompressionSettings.Default,
		LZ4CompressionSettings.Ultra
	})
{
	var start = DateTime.Now;
	// Compress the data
	var result = LZ4Sharp.CompressBytes(dataToCompress, out var compressedData, compressionSetting);
	if (result != LZ4Result.Success)
	{
		Console.WriteLine($"Failed to compress data using settings {compressionSetting}, Reason: {result}.");
		continue;
	}

	// Output the results
	var length = DateTime.Now - start;
	Console.WriteLine($"Compressed {dataToCompress.Length} bytes down to {compressedData.Length} bytes using settings {compressionSetting} [Time {length}].");
}

Console.WriteLine();
Console.WriteLine("Complete");
Console.ReadLine();
// Output:
/*
Compressed 524288 bytes down to 296705 bytes using settings Mode: Fast, Level: Default [Time 00:00:00.2922018].
Compressed 524288 bytes down to 207683 bytes using settings Mode: HighQuality, Level: Default [Time 00:00:03.9848956].
Compressed 524288 bytes down to 195994 bytes using settings Mode: HighQuality, Level: Max [Time 00:00:03.4059679].
*/

ImGui.Wpf

GitHub

About

ImGui.Wpf is a WPF implementation of an immediate mode graphical user interface. Other examples of ImGui implementations are things such as Unity3ds ImGui API and Dear ImGui.

The idea of an immediate mode Gui is to allow a simple API for creating quick and easy user interfaces. In my opinion ImGui implementations lack the advanced features that a full UI API provide (such as something like Xaml), but allow for quick prototyping and mock ups.

Why

The main reason behind this library is that I needed a way to create graphical user interfaces within a scripting system. By which I mean runtime compiled C# being used as a scripting language.

Using C# as a scripting language is made possible with the Roslyn compiler and or by third party libraries such as CS Script. However, runtime compiled Xaml or Forms becomes a little more complicated and also defeats the purpose (in my opinion) in exposing a simple to use scripting API. Thus ImGui.Wpf was born.

State

This is very much still a proof of concept and should not be used in a production environment.

My current plan it to get all the controls implemented and an API fleshed out. Once that has been completed performance and scalability will be investigated and improved.

Example

alt text

var sliderValue = 0.5;
var buffer = "Hello World";

// Create an instance of an imGui panel.
// If no parameter is passed, the main application window is used as the
// hosting control, however you can pass any FrameworkElement which implements
// IAddChild as a hosting control.
using (var imGui = await ImGuiWpf.BeginPanel())
{
    while (true)
    {
        // All ImGui.Wpf calls must be wrapped in a Begin and End frame.
        await imGui.BeginFrame();

        // Add a TextBlock to the UI.
        await imGui.Text("Hello World {0}", 123);
        
        // Add a button to the UI, which will return true if the button was clicked.
        if (await imGui.Button("Save"))
        {
            MessageBox.Show($"You clicked save!\n{buffer}");
        }
        
        // Add a TextBox control, because we are async we can't use 'ref' so have to 
        // pass in the variable and return the potentially updated contents.
        buffer = await imGui.InputText("Input Text:", buffer);
        
        // Add a Slider control with a value, minimum and maximum.
        sliderValue = await imGui.Slider("Slider:", sliderValue, 0.0, 1.0);

        // End the frame.
        await imGui.EndFrame();
    }
}

Implemented Controls

  • Button ✅
  • CheckBox ✅
  • ComboBox ✅
  • Label ✅
  • ListBox ✅
  • ProgressBar ✅
  • RadioButton ✅
  • Slider ✅
  • TextBlock ✅
  • TextBox ✅
  • ToggleButton ✅

Additional Features

  • Custom Styling ❌ (technically, this would be possible via custom controls)
  • Layout Serialization ❌
  • Custom Controls ✅