Built a MonoGame skeleton.

This commit is contained in:
feyris-tan 2025-08-11 19:36:09 +02:00
parent 16b5682fd4
commit 5fdd55f732
28 changed files with 2030 additions and 11 deletions

6
.gitignore vendored
View File

@ -116,3 +116,9 @@ imgui.ini
/.vs/skyscraper8/CopilotIndices/17.14.827.52834
/GUIs/skyscraper8.AnagramViewer/obj
/GUIs/skyscraper8.AnagramViewer/bin/Debug/net8.0-windows
/GUIs/skyscraper8.UI.ImGui.MonoGame.Bridge/bin/Debug/net8.0
/GUIs/skyscraper8.UI.ImGui.MonoGame.Bridge/obj/Debug/net8.0
/GUIs/skyscraper8.UI.ImGui.MonoGame.Bridge/obj
/GUIs/skyscraper8.UI.ImGui.MonoGame/bin/Debug/net8.0
/GUIs/skyscraper8.UI.ImGui.MonoGame/obj/Debug/net8.0
/GUIs/skyscraper8.UI.ImGui.MonoGame/obj

View File

@ -0,0 +1,120 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ImGuiNET.SampleProgram.XNA;
public class BaseGame : Game
{
protected GraphicsDeviceManager _graphics;
protected GraphicsDevice _graphicsDevice;
protected ImGuiRenderer _imGuiRenderer;
protected Random _rng;
protected SpriteBatch _spriteBatch;
protected BaseGame()
{
_graphics = new GraphicsDeviceManager(this);
_rng = new Random();
ChangeResolutionMode(1280, 720, true);
IsMouseVisible = true;
}
protected void ChangeResolutionMode(int width, int height, bool multisampling)
{
_graphics.PreferredBackBufferWidth = width;
_graphics.PreferredBackBufferHeight = height;
_graphics.PreferMultiSampling = multisampling;
_windowBounds = new Rectangle(0, 0, width, height);
}
private Rectangle _windowBounds;
protected Rectangle WindowBounds
{
get
{
return _windowBounds;
}
}
protected override void Initialize()
{
_imGuiRenderer = new ImGuiRenderer(this);
_imGuiRenderer.RebuildFontAtlas();
_imGuiRenderables = new List<ImGuiRenderable>();
_graphicsDevice = _graphics.GraphicsDevice;
_spriteBatch = new SpriteBatch(GraphicsDevice);
_gameObjects = new List<GameObject>();
Window.AllowUserResizing = false;
base.Initialize();
}
protected List<GameObject> _gameObjects;
protected override void Update(GameTime gameTime)
{
Monitor.Enter(_gameObjects);
foreach (GameObject gameObject in _gameObjects)
{
gameObject.Update(gameTime, _graphicsDevice);
}
Monitor.Exit(_gameObjects);
Monitor.Enter(_imGuiRenderables);
for (int i = 0; i < _imGuiRenderables.Count; i++)
{
if (_imGuiRenderables.Count == 0)
continue;
if (_imGuiRenderables[i].WasClosed())
{
_imGuiRenderables[i].Dispose();
_imGuiRenderables.RemoveAt(i);
i = 0;
continue;
}
}
foreach (ImGuiRenderable renderable in _imGuiRenderables)
{
renderable.Update();
}
Monitor.Exit(_imGuiRenderables);
}
protected override void Draw(GameTime gameTime)
{
// Call BeforeLayout first to set things up
_imGuiRenderer.BeforeLayout(gameTime);
//Draw our other stuff.
_spriteBatch.Begin();
Monitor.Enter(_gameObjects);
foreach (GameObject gameObject in _gameObjects)
{
gameObject.Draw(gameTime,_spriteBatch,WindowBounds);
}
Monitor.Exit(_gameObjects);
_spriteBatch.End();
// Draw our UI
ImGuiLayout();
// Call AfterLayout now to finish up and draw all the things
_imGuiRenderer.AfterLayout();
base.Draw(gameTime);
}
protected List<ImGuiRenderable> _imGuiRenderables;
protected virtual void ImGuiLayout()
{
Monitor.Enter(_imGuiRenderables);
foreach (ImGuiRenderable renderable in _imGuiRenderables)
{
renderable.Render();
}
Monitor.Exit(_imGuiRenderables);
Console.WriteLine("X = {0}, Y = {1}", _imGuiRenderer.LastKnownMouseX,_imGuiRenderer.LastKnownMouseY);
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.Xna.Framework.Graphics;
namespace ImGuiNET.SampleProgram.XNA
{
public static class DrawVertDeclaration
{
public static readonly VertexDeclaration Declaration;
public static readonly int Size;
static DrawVertDeclaration()
{
unsafe { Size = sizeof(ImDrawVert); }
Declaration = new VertexDeclaration(
Size,
// Position
new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0),
// UV
new VertexElement(8, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
// Color
new VertexElement(16, VertexElementFormat.Color, VertexElementUsage.Color, 0)
);
}
}
}

View File

@ -0,0 +1,77 @@
using ImGuiNET;
using ImGuiNET.SampleProgram.XNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace MgExample.Bridges;
public class FpsCounterWindow : ImGuiRenderable, GameObject
{
public FpsCounterWindow()
{
open = true;
}
public void Dispose()
{
}
public void Update()
{
}
private bool open;
public void Render()
{
ImGui.Begin("FPS Counter", ref open);
if (!samplingDone)
{
ImGui.Text("Sampling...");
}
else
{
ImGui.Text(String.Format("FPS: {0}", lastCallsPerSecond));
}
ImGui.End();
}
public bool WasClosed()
{
return !open;
}
private bool samplingDone;
private int secondsPassed;
private DateTime lastSeconds;
private int thisCallsPerSecond;
private int lastCallsPerSecond;
public void Update(GameTime gameTime, GraphicsDevice graphicsDevice)
{
thisCallsPerSecond++;
DateTime currentSeconds = DateTime.Now;
if (lastSeconds.Second != currentSeconds.Second)
{
lastCallsPerSecond = thisCallsPerSecond;
secondsPassed++;
lastSeconds = currentSeconds;
thisCallsPerSecond = 0;
if (samplingDone)
{
OnSampleAvailable?.Invoke(currentSeconds, lastCallsPerSecond);
}
}
if (secondsPassed >= 2)
{
samplingDone = true;
}
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds)
{
}
public delegate void SampleAvailable(DateTime time, int fps);
public event SampleAvailable OnSampleAvailable;
}

View File

@ -0,0 +1,10 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ImGuiNET.SampleProgram.XNA;
public interface GameObject
{
void Update(GameTime gameTime, GraphicsDevice graphicsDevice);
void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds);
}

View File

@ -0,0 +1,9 @@
namespace ImGuiNET.SampleProgram.XNA;
public interface ImGuiRenderable : IDisposable
{
void Update();
void Render();
bool WasClosed();
}

View File

@ -0,0 +1,443 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace ImGuiNET.SampleProgram.XNA
{
/// <summary>
/// ImGui renderer for use with XNA-likes (FNA & MonoGame)
/// </summary>
public class ImGuiRenderer
{
private Game _game;
// Graphics
private GraphicsDevice _graphicsDevice;
private BasicEffect _effect;
private RasterizerState _rasterizerState;
private byte[] _vertexData;
private VertexBuffer _vertexBuffer;
private int _vertexBufferSize;
private byte[] _indexData;
private IndexBuffer _indexBuffer;
private int _indexBufferSize;
// Textures
private Dictionary<IntPtr, Texture2D> _loadedTextures;
private int _textureId;
private IntPtr? _fontTextureId;
// Input
private int _scrollWheelValue;
private int _horizontalScrollWheelValue;
private readonly float WHEEL_DELTA = 120;
private Keys[] _allKeys = Enum.GetValues<Keys>();
public ImGuiRenderer(Game game)
{
var context = ImGui.CreateContext();
ImGui.SetCurrentContext(context);
_game = game ?? throw new ArgumentNullException(nameof(game));
_graphicsDevice = game.GraphicsDevice;
_loadedTextures = new Dictionary<IntPtr, Texture2D>();
_rasterizerState = new RasterizerState()
{
CullMode = CullMode.None,
DepthBias = 0,
FillMode = FillMode.Solid,
MultiSampleAntiAlias = false,
ScissorTestEnable = true,
SlopeScaleDepthBias = 0
};
SetupInput();
}
#region ImGuiRenderer
private const bool DISABLE_FONT_ATLAS = false;
/// <summary>
/// Creates a texture and loads the font data from ImGui. Should be called when the <see cref="GraphicsDevice" /> is initialized but before any rendering is done
/// </summary>
public virtual unsafe void RebuildFontAtlas()
{
if (DISABLE_FONT_ATLAS)
return;
// Get font texture from ImGui
var io = ImGui.GetIO();
io.Fonts.GetTexDataAsRGBA32(out byte* pixelData, out int width, out int height, out int bytesPerPixel);
// Copy the data to a managed array
var pixels = new byte[width * height * bytesPerPixel];
unsafe { Marshal.Copy(new IntPtr(pixelData), pixels, 0, pixels.Length); }
// Create and register the texture as an XNA texture
var tex2d = new Texture2D(_graphicsDevice, width, height, false, SurfaceFormat.Color);
tex2d.SetData(pixels);
// Should a texture already have been build previously, unbind it first so it can be deallocated
if (_fontTextureId.HasValue) UnbindTexture(_fontTextureId.Value);
// Bind the new texture to an ImGui-friendly id
_fontTextureId = BindTexture(tex2d);
// Let ImGui know where to find the texture
io.Fonts.SetTexID(_fontTextureId.Value);
io.Fonts.ClearTexData(); // Clears CPU side texture data
}
/// <summary>
/// Creates a pointer to a texture, which can be passed through ImGui calls such as <see cref="ImGui.Image" />. That pointer is then used by ImGui to let us know what texture to draw
/// </summary>
public virtual IntPtr BindTexture(Texture2D texture)
{
var id = new IntPtr(_textureId++);
_loadedTextures.Add(id, texture);
return id;
}
/// <summary>
/// Removes a previously created texture pointer, releasing its reference and allowing it to be deallocated
/// </summary>
public virtual void UnbindTexture(IntPtr textureId)
{
_loadedTextures.Remove(textureId);
}
/// <summary>
/// Sets up ImGui for a new frame, should be called at frame start
/// </summary>
public virtual void BeforeLayout(GameTime gameTime)
{
ImGui.GetIO().DeltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
UpdateInput();
ImGui.NewFrame();
}
/// <summary>
/// Asks ImGui for the generated geometry data and sends it to the graphics pipeline, should be called after the UI is drawn using ImGui.** calls
/// </summary>
public virtual void AfterLayout()
{
ImGui.Render();
unsafe { RenderDrawData(ImGui.GetDrawData()); }
}
#endregion ImGuiRenderer
#region Setup & Update
/// <summary>
/// Setup key input event handler.
/// </summary>
protected virtual void SetupInput()
{
var io = ImGui.GetIO();
// MonoGame-specific //////////////////////
_game.Window.TextInput += (s, a) =>
{
if (a.Character == '\t') return;
io.AddInputCharacter(a.Character);
};
///////////////////////////////////////////
// FNA-specific ///////////////////////////
//TextInputEXT.TextInput += c =>
//{
// if (c == '\t') return;
// ImGui.GetIO().AddInputCharacter(c);
//};
///////////////////////////////////////////
}
/// <summary>
/// Updates the <see cref="Effect" /> to the current matrices and texture
/// </summary>
protected virtual Effect UpdateEffect(Texture2D texture)
{
_effect = _effect ?? new BasicEffect(_graphicsDevice);
var io = ImGui.GetIO();
_effect.World = Matrix.Identity;
_effect.View = Matrix.Identity;
_effect.Projection = Matrix.CreateOrthographicOffCenter(0f, io.DisplaySize.X, io.DisplaySize.Y, 0f, -1f, 1f);
_effect.TextureEnabled = true;
_effect.Texture = texture;
_effect.VertexColorEnabled = true;
return _effect;
}
public int LastKnownMouseX { get; set; }
public int LastKnownMouseY { get; set; }
/// <summary>
/// Sends XNA input state to ImGui
/// </summary>
protected virtual void UpdateInput()
{
if (!_game.IsActive) return;
var io = ImGui.GetIO();
var mouse = Mouse.GetState();
LastKnownMouseX = mouse.X;
LastKnownMouseY = mouse.Y;
var keyboard = Keyboard.GetState();
io.AddMousePosEvent(mouse.X, mouse.Y);
io.AddMouseButtonEvent(0, mouse.LeftButton == ButtonState.Pressed);
io.AddMouseButtonEvent(1, mouse.RightButton == ButtonState.Pressed);
io.AddMouseButtonEvent(2, mouse.MiddleButton == ButtonState.Pressed);
io.AddMouseButtonEvent(3, mouse.XButton1 == ButtonState.Pressed);
io.AddMouseButtonEvent(4, mouse.XButton2 == ButtonState.Pressed);
io.AddMouseWheelEvent(
(mouse.HorizontalScrollWheelValue - _horizontalScrollWheelValue) / WHEEL_DELTA,
(mouse.ScrollWheelValue - _scrollWheelValue) / WHEEL_DELTA);
_scrollWheelValue = mouse.ScrollWheelValue;
_horizontalScrollWheelValue = mouse.HorizontalScrollWheelValue;
foreach (var key in _allKeys)
{
if (TryMapKeys(key, out ImGuiKey imguikey))
{
io.AddKeyEvent(imguikey, keyboard.IsKeyDown(key));
}
}
io.DisplaySize = new System.Numerics.Vector2(_graphicsDevice.PresentationParameters.BackBufferWidth, _graphicsDevice.PresentationParameters.BackBufferHeight);
io.DisplayFramebufferScale = new System.Numerics.Vector2(1f, 1f);
}
private bool TryMapKeys(Keys key, out ImGuiKey imguikey)
{
//Special case not handed in the switch...
//If the actual key we put in is "None", return none and true.
//otherwise, return none and false.
if (key == Keys.None)
{
imguikey = ImGuiKey.None;
return true;
}
imguikey = key switch
{
Keys.Back => ImGuiKey.Backspace,
Keys.Tab => ImGuiKey.Tab,
Keys.Enter => ImGuiKey.Enter,
Keys.CapsLock => ImGuiKey.CapsLock,
Keys.Escape => ImGuiKey.Escape,
Keys.Space => ImGuiKey.Space,
Keys.PageUp => ImGuiKey.PageUp,
Keys.PageDown => ImGuiKey.PageDown,
Keys.End => ImGuiKey.End,
Keys.Home => ImGuiKey.Home,
Keys.Left => ImGuiKey.LeftArrow,
Keys.Right => ImGuiKey.RightArrow,
Keys.Up => ImGuiKey.UpArrow,
Keys.Down => ImGuiKey.DownArrow,
Keys.PrintScreen => ImGuiKey.PrintScreen,
Keys.Insert => ImGuiKey.Insert,
Keys.Delete => ImGuiKey.Delete,
>= Keys.D0 and <= Keys.D9 => ImGuiKey._0 + (key - Keys.D0),
>= Keys.A and <= Keys.Z => ImGuiKey.A + (key - Keys.A),
>= Keys.NumPad0 and <= Keys.NumPad9 => ImGuiKey.Keypad0 + (key - Keys.NumPad0),
Keys.Multiply => ImGuiKey.KeypadMultiply,
Keys.Add => ImGuiKey.KeypadAdd,
Keys.Subtract => ImGuiKey.KeypadSubtract,
Keys.Decimal => ImGuiKey.KeypadDecimal,
Keys.Divide => ImGuiKey.KeypadDivide,
>= Keys.F1 and <= Keys.F24 => ImGuiKey.F1 + (key - Keys.F1),
Keys.NumLock => ImGuiKey.NumLock,
Keys.Scroll => ImGuiKey.ScrollLock,
Keys.LeftShift => ImGuiKey.LeftShift,
Keys.LeftControl => ImGuiKey.LeftCtrl,
Keys.LeftAlt => ImGuiKey.LeftAlt,
Keys.OemSemicolon => ImGuiKey.Semicolon,
Keys.OemPlus => ImGuiKey.Equal,
Keys.OemComma => ImGuiKey.Comma,
Keys.OemMinus => ImGuiKey.Minus,
Keys.OemPeriod => ImGuiKey.Period,
Keys.OemQuestion => ImGuiKey.Slash,
Keys.OemTilde => ImGuiKey.GraveAccent,
Keys.OemOpenBrackets => ImGuiKey.LeftBracket,
Keys.OemCloseBrackets => ImGuiKey.RightBracket,
Keys.OemPipe => ImGuiKey.Backslash,
Keys.OemQuotes => ImGuiKey.Apostrophe,
_ => ImGuiKey.None,
};
return imguikey != ImGuiKey.None;
}
#endregion Setup & Update
#region Internals
/// <summary>
/// Gets the geometry as set up by ImGui and sends it to the graphics device
/// </summary>
private void RenderDrawData(ImDrawDataPtr drawData)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers
var lastViewport = _graphicsDevice.Viewport;
var lastScissorBox = _graphicsDevice.ScissorRectangle;
var lastRasterizer = _graphicsDevice.RasterizerState;
var lastDepthStencil = _graphicsDevice.DepthStencilState;
var lastBlendFactor = _graphicsDevice.BlendFactor;
var lastBlendState = _graphicsDevice.BlendState;
_graphicsDevice.BlendFactor = Color.White;
_graphicsDevice.BlendState = BlendState.NonPremultiplied;
_graphicsDevice.RasterizerState = _rasterizerState;
_graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
// Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays)
drawData.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
// Setup projection
_graphicsDevice.Viewport = new Viewport(0, 0, _graphicsDevice.PresentationParameters.BackBufferWidth, _graphicsDevice.PresentationParameters.BackBufferHeight);
UpdateBuffers(drawData);
RenderCommandLists(drawData);
// Restore modified state
_graphicsDevice.Viewport = lastViewport;
_graphicsDevice.ScissorRectangle = lastScissorBox;
_graphicsDevice.RasterizerState = lastRasterizer;
_graphicsDevice.DepthStencilState = lastDepthStencil;
_graphicsDevice.BlendState = lastBlendState;
_graphicsDevice.BlendFactor = lastBlendFactor;
}
private unsafe void UpdateBuffers(ImDrawDataPtr drawData)
{
if (drawData.TotalVtxCount == 0)
{
return;
}
// Expand buffers if we need more room
if (drawData.TotalVtxCount > _vertexBufferSize)
{
_vertexBuffer?.Dispose();
_vertexBufferSize = (int)(drawData.TotalVtxCount * 1.5f);
_vertexBuffer = new VertexBuffer(_graphicsDevice, DrawVertDeclaration.Declaration, _vertexBufferSize, BufferUsage.None);
_vertexData = new byte[_vertexBufferSize * DrawVertDeclaration.Size];
}
if (drawData.TotalIdxCount > _indexBufferSize)
{
_indexBuffer?.Dispose();
_indexBufferSize = (int)(drawData.TotalIdxCount * 1.5f);
_indexBuffer = new IndexBuffer(_graphicsDevice, IndexElementSize.SixteenBits, _indexBufferSize, BufferUsage.None);
_indexData = new byte[_indexBufferSize * sizeof(ushort)];
}
// Copy ImGui's vertices and indices to a set of managed byte arrays
int vtxOffset = 0;
int idxOffset = 0;
for (int n = 0; n < drawData.CmdListsCount; n++)
{
ImDrawListPtr cmdList = drawData.CmdLists[n];
fixed (void* vtxDstPtr = &_vertexData[vtxOffset * DrawVertDeclaration.Size])
fixed (void* idxDstPtr = &_indexData[idxOffset * sizeof(ushort)])
{
Buffer.MemoryCopy((void*)cmdList.VtxBuffer.Data, vtxDstPtr, _vertexData.Length, cmdList.VtxBuffer.Size * DrawVertDeclaration.Size);
Buffer.MemoryCopy((void*)cmdList.IdxBuffer.Data, idxDstPtr, _indexData.Length, cmdList.IdxBuffer.Size * sizeof(ushort));
}
vtxOffset += cmdList.VtxBuffer.Size;
idxOffset += cmdList.IdxBuffer.Size;
}
// Copy the managed byte arrays to the gpu vertex- and index buffers
_vertexBuffer.SetData(_vertexData, 0, drawData.TotalVtxCount * DrawVertDeclaration.Size);
_indexBuffer.SetData(_indexData, 0, drawData.TotalIdxCount * sizeof(ushort));
}
private unsafe void RenderCommandLists(ImDrawDataPtr drawData)
{
_graphicsDevice.SetVertexBuffer(_vertexBuffer);
_graphicsDevice.Indices = _indexBuffer;
int vtxOffset = 0;
int idxOffset = 0;
for (int n = 0; n < drawData.CmdListsCount; n++)
{
ImDrawListPtr cmdList = drawData.CmdLists[n];
for (int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++)
{
ImDrawCmdPtr drawCmd = cmdList.CmdBuffer[cmdi];
if (drawCmd.ElemCount == 0)
{
continue;
}
if (!_loadedTextures.ContainsKey(drawCmd.TextureId))
{
throw new InvalidOperationException($"Could not find a texture with id '{drawCmd.TextureId}', please check your bindings");
}
_graphicsDevice.ScissorRectangle = new Rectangle(
(int)drawCmd.ClipRect.X,
(int)drawCmd.ClipRect.Y,
(int)(drawCmd.ClipRect.Z - drawCmd.ClipRect.X),
(int)(drawCmd.ClipRect.W - drawCmd.ClipRect.Y)
);
var effect = UpdateEffect(_loadedTextures[drawCmd.TextureId]);
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
#pragma warning disable CS0618 // // FNA does not expose an alternative method.
_graphicsDevice.DrawIndexedPrimitives(
primitiveType: PrimitiveType.TriangleList,
baseVertex: (int)drawCmd.VtxOffset + vtxOffset,
minVertexIndex: 0,
numVertices: cmdList.VtxBuffer.Size,
startIndex: (int)drawCmd.IdxOffset + idxOffset,
primitiveCount: (int)drawCmd.ElemCount / 3
);
#pragma warning restore CS0618
}
}
vtxOffset += cmdList.VtxBuffer.Size;
idxOffset += cmdList.IdxBuffer.Size;
}
}
#endregion Internals
}
}

View File

@ -0,0 +1,41 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ImGuiNET.SampleProgram.XNA;
public class IqWindow : TextureDisplayWindow
{
public IqWindow(GraphicsDevice graphicsDevice, ImGuiRenderer renderer, string name = "IQ", bool closeable = false)
: base(renderer, new Texture2D(graphicsDevice,256,256), name, closeable)
{
sampleQueue = new Queue<Tuple<byte, byte>>();
colorBuffer = new Color[256 * 256];
zBuffer = new byte[256 * 256];
_texture.GetData(colorBuffer);
Array.Fill(colorBuffer, Color.Transparent);
}
private Queue<Tuple<byte, byte>> sampleQueue;
public void EnqueueSample(byte i, byte q)
{
sampleQueue.Enqueue(new Tuple<byte, byte>(i, q));
}
private byte[] zBuffer;
private Color[] colorBuffer;
public override void Update()
{
while (sampleQueue.Count > 0)
{
Tuple<byte, byte> dequeue = sampleQueue.Dequeue();
int offset = dequeue.Item2 * 256;
offset += dequeue.Item1;
byte z = zBuffer[offset]++;
colorBuffer[offset] = new Color(z, 0, 255 - z, 255);
}
_texture.SetData(colorBuffer);
base.Update();
}
}

View File

@ -0,0 +1,9 @@
namespace ImGuiNET.SampleProgram.XNA;
public static class RandomExtension
{
public static T NextItem<T>(this Random rng, T[] array)
{
return array[rng.Next(array.Length)];
}
}

View File

@ -0,0 +1,62 @@
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework.Graphics;
using ScottPlot;
using SkiaSharp;
namespace ImGuiNET.SampleProgram.XNA;
public class ScottPlotWindow : TextureDisplayWindow
{
public Plot Plot { get; private set; }
private SKImageInfo _imageInfo;
private SKBitmap _bitmap;
private SKCanvas _canvas;
public ScottPlotWindow(ImGuiRenderer renderer, GraphicsDevice graphicsDevice, Plot plot, int width, int height, string name = null, bool closeable = false)
: base(renderer, new Texture2D(graphicsDevice,width,height), name, closeable)
{
if (renderer == null)
throw new ArgumentNullException(nameof(renderer));
if (graphicsDevice == null)
throw new ArgumentNullException(nameof(graphicsDevice));
if (plot == null)
throw new ArgumentNullException(nameof(plot));
Plot = plot;
_imageInfo = new SKImageInfo(width, height, GuessSKColorType(graphicsDevice));
_bitmap = new SKBitmap(width, height);
_canvas = new SKCanvas(_bitmap);
}
private SKColorType GuessSKColorType(GraphicsDevice graphicsDevice)
{
SurfaceFormat xnaSurfaceFormat = graphicsDevice.PresentationParameters.BackBufferFormat;
switch (xnaSurfaceFormat)
{
case SurfaceFormat.Color:
return SKColorType.Rgba8888;
default:
throw new NotImplementedException(xnaSurfaceFormat.ToString());
}
}
private byte[] buffer;
public override void Update()
{
Plot.Render(_canvas, _imageInfo.Width, _imageInfo.Height);
IntPtr pixelPointer = _bitmap.GetPixels();
int size = (_bitmap.Width * _bitmap.Height) * 4;
if (buffer == null)
buffer = new byte[size];
Marshal.Copy(pixelPointer, buffer, 0, size);
_texture.SetData(buffer);
base.Update();
}
}

View File

@ -0,0 +1,79 @@
using System.Numerics;
using Microsoft.Xna.Framework.Graphics;
using ScottPlot.Plottables;
namespace ImGuiNET.SampleProgram.XNA;
public class TextureDisplayWindow : ImGuiRenderable
{
protected Texture2D _texture;
protected string _name;
protected bool open;
private bool closeable;
protected ImGuiRenderer _renderer;
public TextureDisplayWindow(ImGuiRenderer renderer, Texture2D texture, string name = null, bool closeable = false)
{
if (renderer == null)
throw new ArgumentNullException(nameof(renderer));
_renderer = renderer;
this._texture = texture;
this._name = name;
if (string.IsNullOrEmpty(name))
{
this._name = texture.ToString();
}
this.open = true;
this.closeable = closeable;
}
private bool bound;
private IntPtr _texturePointer;
protected System.Numerics.Vector2 windowSize;
public virtual void Update()
{
if (_texture == null)
return;
if (!bound)
{
_texturePointer = _renderer.BindTexture(_texture);
windowSize = new Vector2(_texture.Width, _texture.Height);
bound = true;
}
}
public void Render()
{
if (!bound)
return;
ImGui.SetNextWindowSize(windowSize);
if (closeable)
ImGui.Begin(_name, ref open);
else
ImGui.Begin(_name);
ImGui.Image(_texturePointer, windowSize);
ImGui.End();
}
public bool WasClosed()
{
return !open;
}
public void Dispose()
{
if (bound)
{
_renderer.UnbindTexture(_texturePointer);
bound = false;
}
_texture.Dispose();
}
}

View File

@ -0,0 +1,186 @@
namespace ConsoleApp1.Math;
//This one is heavily inspired from https://github.com/colgreen/Redzen/blob/main/Redzen/Numerics/Distributions/Double/ZigguratGaussian.cs
//To understand how this algorithm works, please read those Github comments.
using System.Diagnostics;
using System.Runtime.CompilerServices;
public class Ziggurat
{
const int __blockCount = 128;
const double __R = 3.442619855899;
const double __A = 9.91256303526217e-3;
const ulong __MAXINT = (1UL << 53) - 1;
const double __INCR = 1.0 / __MAXINT;
private double[] __x;
private double[] __y;
private ulong[] __xComp;
private double __A_Div_Y0;
private Random rng;
public Ziggurat()
: this(null)
{
rng = new Random((int)DateTime.Now.Ticks);
}
public Ziggurat(int seed)
: this(null)
{
rng = new Random(seed);
}
public Ziggurat(Random randomSrc)
{
this.rng = randomSrc;
// Initialise rectangle position data.
// __x[i] and __y[i] describe the top-right position of Box i.
// Allocate storage. We add one to the length of _x so that we have an entry at __x[__blockCount], this avoids having
// to do a special case test when sampling from the top box.
__x = new double[__blockCount + 1];
__y = new double[__blockCount];
// Determine top right position of the base rectangle/box (the rectangle with the Gaussian tale attached).
// We call this Box 0 or B0 for short.
// Note. x[0] also describes the right-hand edge of B1. (See diagram).
__x[0] = __R;
__y[0] = GaussianPdfDenorm(__R);
// The next box (B1) has a right hand X edge the same as B0.
// Note. B1's height is the box area divided by its width, hence B1 has a smaller height than B0 because
// B0's total area includes the attached distribution tail.
__x[1] = __R;
__y[1] = __y[0] + (__A / __x[1]);
// Calc positions of all remaining rectangles.
for(int i=2; i < __blockCount; i++)
{
__x[i] = GaussianPdfDenormInv(__y[i-1]);
__y[i] = __y[i-1] + (__A / __x[i]);
}
// For completeness we define the right-hand edge of a notional box 6 as being zero (a box with no area).
__x[__blockCount] = 0.0;
// Useful precomputed values.
__A_Div_Y0 = __A / __y[0];
__xComp = new ulong[__blockCount];
// Special case for base box. __xComp[0] stores the area of B0 as a proportion of __R
// (recalling that all segments have area __A, but that the base segment is the combination of B0 and the distribution tail).
// Thus __xComp[0] is the probability that a sample point is within the box part of the segment.
__xComp[0] = (ulong)(((__R * __y[0]) / __A) * (double)__MAXINT);
for(int i=1; i < __blockCount-1; i++)
{
__xComp[i] = (ulong)((__x[i+1] / __x[i]) * (double)__MAXINT);
}
__xComp[__blockCount-1] = 0; // Shown for completeness.
// Sanity check. Test that the top edge of the topmost rectangle is at y=1.0.
// Note. We expect there to be a tiny drift away from 1.0 due to the inexactness of floating
// point arithmetic.
Debug.Assert(System.Math.Abs(1.0 - __y[__blockCount-1]) < 1e-10);
}
private static double GaussianPdfDenorm(double x)
{
return System.Math.Exp(-(x * x * 0.5));
}
private static double GaussianPdfDenormInv(double y)
{
// Operates over the y interval (0,1], which happens to be the y interval of the pdf,
// with the exception that it does not include y=0, but we would never call with
// y=0 so it doesn't matter. Note that a Gaussian effectively has a tail going
// into infinity on the x-axis, hence asking what is x when y=0 is an invalid question
// in the context of this class.
return System.Math.Sqrt(-2.0 * System.Math.Log(y));
}
private void SampleTail(out double x)
{
double y;
do
{
// Note. we use NextDoubleNonZero() because Log(0) returns -Infinity and will also tend to be a very slow execution path (when it occurs, which is rarely).
x = -System.Math.Log(rng.NextDouble()) / __R;
y = -System.Math.Log(rng.NextDouble());
}
while(y + y < x * x);
x += __R;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetSignBit(ref double x, ref ulong signBit)
{
Unsafe.As<double, ulong>(ref x) |= signBit;
}
public double Sample()
{
double x = 0.0;
while(true)
{
// Generate 64 random bits.
ulong u = (ulong)rng.NextInt64();
// Note. 61 random bits are required and therefore the lowest three bits are discarded
// (a typical characteristic of PRNGs is that the least significant bits exhibit lower
// quality randomness than the higher bits).
// Note. The bit index values in comments are zero based, i..e the first bit is bit 0.
// Select a segment (7 bits, bits 3 to 9).
int s = (int)((u >> 3) & 0x7f);
// Select the sign bit (bit 10), and shift it to the sign bit position for IEEE754
// double-precision floating-point format.
// Previously, the sign bit handling used a conditional branch that optionally multiplied the
// positive Gaussian sample by -1. However, that approach is considerably slower because modern
// superscalar CPUs rely heavily on branch prediction, but the sign bit here is randomly generated
// and therefore entirely unpredictable, i.e. the absolute worse case scenario for a branch
// predictor!
ulong signBit = (u & 0x400UL) << 53;
// Get a uniform random value with interval [0, 2^53-1], or in hexadecimal [0, 0x1f_ffff_ffff_ffff]
// (i.e. a random 53 bit number) (bits 11 to 63).
ulong u2 = u >> 11;
// Special case for the base segment.
if(s == 0)
{
if(u2 < __xComp[0])
{
// Generated x is within R0.
x = u2 * __INCR * __A_Div_Y0;
}
else
{
// Generated x is in the tail of the distribution.
SampleTail(out x);
}
SetSignBit(ref x, ref signBit);
return x;
}
// All other segments.
if(u2 < __xComp[s])
{
// Generated x is within the rectangle.
x = u2 * __INCR * __x[s];
SetSignBit(ref x, ref signBit);
return x;
}
// Generated x is outside of the rectangle.
// Generate a random y coordinate and test if our (x,y) is within the distribution curve.
// This execution path is relatively slow/expensive (makes a call to Math.Exp()) but is relatively rarely executed,
// although more often than the 'tail' path (above).
x = u2 * __INCR * __x[s];
if(__y[s-1] + ((__y[s] - __y[s-1]) * rng.NextDouble()) < GaussianPdfDenorm(x))
{
SetSignBit(ref x, ref signBit);
return x;
}
}
}
}

View File

@ -0,0 +1,83 @@
using System.Diagnostics;
using ConsoleApp1.Math;
namespace ConsoleApp1.IO;
public class ZigguratStream : Stream
{
public ZigguratStream()
{
_ziggurat = new Ziggurat();
}
public ZigguratStream(int seed)
{
_ziggurat = new Ziggurat(seed);
}
public ZigguratStream(Random random)
{
_ziggurat = new Ziggurat(random);
}
private Ziggurat _ziggurat;
private double minZig, maxZig;
private byte getNextByte()
{
double sample = _ziggurat.Sample();
double oldMin = minZig;
double oldMax = maxZig;
minZig = System.Math.Min(minZig, sample);
maxZig = System.Math.Max(maxZig, sample);
if (oldMin != minZig || oldMax != maxZig)
{
return getNextByte();
}
sample += (minZig / -1.0);
double span = (minZig / -1.0) + maxZig;
double sampleScaled = sample / span;
Debug.Assert(sampleScaled <= 1.0);
sampleScaled *= 255.0;
byte scaledByte = (byte)sampleScaled;
return scaledByte;
}
public override void Flush()
{
throw new NotSupportedException();
}
private long internalPosition;
public override int Read(byte[] buffer, int offset, int count)
{
for (int i = 0; i < count; i++)
{
buffer[offset + i] = getNextByte();
internalPosition++;
}
return count;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position { get => internalPosition; set => throw new NotSupportedException(); }
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ImGui.NET" Version="1.91.6.1" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.4" />
<PackageReference Include="ScottPlot" Version="5.0.55" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
using skyscraper8.UI.ImGui.MonoGame;
namespace moe.yo3explorer.skyscraper8.UI.MonoGame
{
class Program
{
public static void Main(string[] args)
{
SkyscraperGame game = new SkyscraperGame();
game.Run();
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.Plugins;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
[PluginPriority(0)]
[DisplayName("Blank")]
internal class _0_Blank : IScreenhack
{
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds)
{
}
public void SetGraphicsDevice(GraphicsDevice graphicsDevice, Rectangle presentationParametersBounds)
{
}
public void Dispose()
{
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Threading.Tasks;
using ImGuiNET.SampleProgram.XNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using skyscraper5.Skyscraper.Plugins;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
[PluginPriority(1)]
[DisplayName("Your Waifu")]
internal class _1_YourWaifu : IScreenhack
{
private bool disabled;
private FileInfo fileInfo;
public _1_YourWaifu()
{
DirectoryInfo di = new DirectoryInfo("wallpapers");
if (!di.Exists)
{
disabled = true;
return;
}
FileInfo[] files = di.GetFiles("*.png");
fileInfo = new Random().NextItem(files);
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds)
{
if (disabled)
return;
if (texture2D == null)
return;
spriteBatch.Draw(texture2D, windowBounds, Color.White);
}
private Texture2D texture2D;
public void SetGraphicsDevice(GraphicsDevice graphicsDevice, Rectangle presentationParametersBounds)
{
if (disabled)
return;
texture2D = Texture2D.FromFile(graphicsDevice, fileInfo.FullName);
}
public void Dispose()
{
texture2D.Dispose();
}
}
}

View File

@ -0,0 +1,372 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using skyscraper5.Skyscraper.Plugins;
using Color = System.Drawing.Color;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
[PluginPriority(2)]
[DisplayName("Substrate")]
internal class _2_Substrate : Processing, IScreenhack
{
int dimx = 250;
int dimy = 250;
int num = 0;
int maxnum = 100;
// grid of cracks
int[] cgrid;
Crack[] cracks;
// color parameters
int maxpal = 512;
int numpal = 0;
private Color[] goodcolor;
// sand painters
SandPainter[] sands;
private void Begin()
{
// erase crack grid
for (int y = 0; y < dimy; y++)
{
for (int x = 0; x < dimx; x++)
{
cgrid[y * dimx + x] = 10001;
}
}
// make random crack seeds
for (int k = 0; k < 16; k++)
{
int i = (Random(dimx * dimy - 1));
cgrid[i] = (Random(360));
}
// make just three cracks
num = 0;
for (int k = 0; k < 3; k++)
{
MakeCrack();
}
//Background(Color.FromArgb(255, 255, 255, 255));
}
private void MakeCrack()
{
if (num < maxnum)
{
// make a new crack instance
cracks[num] = new Crack(this);
num++;
}
}
private void TakeColor()
{
uint[] srcArrays =
{
0x201F21, 0x262C2E, 0x352626, 0x372B27,
0x302C2E, 0x392B2D, 0x323229, 0x3F3229,
0x38322E, 0x2E333D, 0x333A3D, 0x473329,
0x40392C, 0x40392E, 0x47402C, 0x47402E,
0x4E402C, 0x4F402E, 0x4E4738, 0x584037,
0x65472D, 0x6D5D3D, 0x745530, 0x755532,
0x745D32, 0x746433, 0x7C6C36, 0x523152,
0x444842, 0x4C5647, 0x655D45, 0x6D5D44,
0x6C5D4E, 0x746C43, 0x7C6C42, 0x7C6C4B,
0x6B734B, 0x73734B, 0x7B7B4A, 0x6B6C55,
0x696D5E, 0x7B6C5D, 0x6B7353, 0x6A745D,
0x727B52, 0x7B7B52, 0x57746E, 0x687466,
0x9C542B, 0x9D5432, 0x9D5B35, 0x936B36,
0xAA7330, 0xC45A27, 0xD95223, 0xD85A20,
0xDB5A23, 0xE57037, 0x836C4B, 0x8C6B4B,
0x82735C, 0x937352, 0x817B63, 0x817B6D,
0x927B63, 0xD9893B, 0xE49832, 0xDFA133,
0xE5A037, 0xF0AB3B, 0x8A8A59, 0xB29A58,
0x89826B, 0x9A8262, 0x888B7C, 0x909A7A,
0xA28262, 0xA18A69, 0xA99968, 0x99A160,
0x99A168, 0xCA8148, 0xEB8D43, 0xC29160,
0xC29168, 0xD1A977, 0xC9B97F, 0xF0E27B,
0x9F928B, 0xC0B999, 0xE6B88F, 0xC8C187,
0xE0C886, 0xF2CC85, 0xF5DA83, 0xECDE9D,
0xF5D294, 0xF5DA94, 0xF4E784, 0xF4E18A,
0xF4E193, 0xE7D8A7, 0xF1D4A5, 0xF1DCA5,
0xF4DBAD, 0xF1DCAE, 0xF4DBB5, 0xF5DBBD,
0xF4E2AD, 0xF5E9AD, 0xF4E3BE, 0xF5EABE,
0xF7F0B6, 0xD9D1C1, 0xE0D0C0, 0xE7D8C0,
0xF1DDC6, 0xE8E1C0, 0xF3EDC7, 0xF6ECCE,
0xF8F2C7, 0xEFEFD0, 0
};
for (int i = 0; i < srcArrays.Length; i++)
{
srcArrays[i] |= 0xff000000;
goodcolor[i] = Color.FromArgb((int)srcArrays[i]);
}
numpal = srcArrays.Length;
}
class Crack
{
private readonly _2_Substrate _parent;
private float x, y, t;
private SandPainter sp;
public Crack(_2_Substrate parent)
{
_parent = parent;
FindStart();
sp = new SandPainter(_parent);
}
private void FindStart()
{
// pick random point
int px = 0;
int py = 0;
// shift until crack is found
bool found = false;
int timeout = 0;
while ((!found) || (timeout++ > 1000))
{
px = (_parent.Random(_parent.dimx));
py = (_parent.Random(_parent.dimy));
if (_parent.cgrid[py * _parent.dimx + px] < 10000)
{
found = true;
}
}
if (found)
{
// start crack
int a = _parent.cgrid[py * _parent.dimx + px];
if (_parent.Random(100) < 50)
{
a -= 90 + (int)(_parent.Random(-2.0f, 2.1f));
}
else
{
a += 90 + (int)(_parent.Random(-2.0f, 2.1f));
}
StartCrack(px, py, a);
}
else
{
//println("timeout: "+timeout);
}
}
private void StartCrack(int X, int Y, int T)
{
x = X;
y = Y;
t = T; //%360;
x += 0.61f * (float)Math.Cos(t * Math.PI / 180.0);
y += 0.61f * (float)Math.Sin(t * Math.PI / 180.0);
}
public void Move()
{
// continue cracking
x += 0.42f * (float)Math.Cos(t * Math.PI / 180);
y += 0.42f * (float)Math.Sin(t * Math.PI / 180);
// bound check
float z = 0.33f;
int cx = (int)(x + _parent.Random(-z, z)); // add fuzz
int cy = (int)(y + _parent.Random(-z, z));
// draw sand painter
RegionColor();
// draw black crack
_parent.Stroke(0.0f, 85.0f);
_parent.Point(x + _parent.Random(-z, z), y + _parent.Random(-z, z));
if ((cx >= 0) && (cx < _parent.dimx) && (cy >= 0) && (cy < _parent.dimy))
{
// safe to check
if ((_parent.cgrid[cy * _parent.dimx + cx] > 10000) ||
(Math.Abs(_parent.cgrid[cy * _parent.dimx + cx] - t) < 5))
{
// continue cracking
_parent.cgrid[cy * _parent.dimx + cx] = (int)(t);
}
else if (Math.Abs(_parent.cgrid[cy * _parent.dimx + cx] - t) > 2)
{
// crack encountered (not self), stop cracking
FindStart();
_parent.MakeCrack();
}
}
else
{
// out of bounds, stop cracking
FindStart();
_parent.MakeCrack();
}
}
private void RegionColor()
{
// start checking one step away
float rx = x;
float ry = y;
bool openspace = true;
// find extents of open space
while (openspace)
{
// move perpendicular to crack
rx += 0.81f * (float)Math.Sin(t * Math.PI / 180.0);
ry -= 0.81f * (float)Math.Cos(t * Math.PI / 180.0);
int cx = (int)(rx);
int cy = (int)(ry);
if ((cx >= 0) && (cx < _parent.dimx) && (cy >= 0) && (cy < _parent.dimy))
{
// safe to check
if (_parent.cgrid[cy * _parent.dimx + cx] > 10000)
{
// space is open
}
else
{
openspace = false;
}
}
else
{
openspace = false;
}
}
// draw sand painter
sp.Render(rx, ry, x, y);
}
}
class SandPainter
{
private readonly _2_Substrate _parent;
public SandPainter(_2_Substrate _parent)
{
this._parent = _parent;
c = _parent.SomeColor();
g = _parent.Random(0.01f, 0.1f);
}
private Color c;
private float g;
public void Render(float x, float y, float ox, float oy)
{
// modulate gain
g += _parent.Random(-0.050f, 0.050f);
float maxg = 1.0f;
if (g < 0) g = 0;
if (g > maxg) g = maxg;
// calculate grains by distance
//int grains = (int)(Math.Sqrt((ox-x)*(ox-x)+(oy-y)*(oy-y)));
int grains = 64;
// lay down grains of sand (transparent pixels)
float w = g / (grains - 1);
for (int i = 0; i < grains; i++)
{
float a = 0.1f - i / (grains * 10.0f);
_parent.Stroke(c.R, c.G, c.B, a * 256);
_parent.Point(ox + (x - ox) * (float)Math.Sin(Math.Sin(i * w)),
oy + (y - oy) * (float)Math.Sin(Math.Sin(i * w)));
}
}
}
private Color SomeColor()
{
if (rng == null)
rng = new Random();
return goodcolor[rng.Next(numpal)];
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds)
{
for (int n = 0; n < num; n++)
{
cracks[n].Move();
}
texture.SetData(textureColors);
spriteBatch.Draw(texture, windowBounds, Microsoft.Xna.Framework.Color.White);
}
private Texture2D texture;
private Microsoft.Xna.Framework.Color[] textureColors;
private int screenWidth, screenHeight;
public void SetGraphicsDevice(GraphicsDevice graphicsDevice, Rectangle presentationParametersBounds)
{
screenWidth = presentationParametersBounds.Width;
screenHeight = presentationParametersBounds.Height;
texture = new Texture2D(graphicsDevice, presentationParametersBounds.Width,
presentationParametersBounds.Height);
textureColors =
new Microsoft.Xna.Framework.Color[presentationParametersBounds.Width *
presentationParametersBounds.Height];
texture.GetData(textureColors);
for (int i = 0; i < textureColors.Length; i++)
{
textureColors[i] = Microsoft.Xna.Framework.Color.White;
}
goodcolor = new Color[maxpal];
//Background(Color.FromArgb(255, 255, 255, 255));
TakeColor();
dimx = presentationParametersBounds.Width;
dimy = presentationParametersBounds.Height;
cgrid = new int[dimx * dimy];
cracks = new Crack[maxnum];
Begin();
}
public void Dispose()
{
}
protected override void Point(float x, float y)
{
int ix = (int)x;
int iy = (int)y;
if (y < 0)
return;
if (y >= screenHeight)
return;
if (x < 0)
return;
if (x > screenWidth)
return;
textureColors[(iy * screenWidth) + ix].A = strokeColorObject.A;
textureColors[(iy * screenWidth) + ix].R = strokeColorObject.R;
textureColors[(iy * screenWidth) + ix].G = strokeColorObject.G;
textureColors[(iy * screenWidth) + ix].B = strokeColorObject.B;
}
}
}

View File

@ -0,0 +1,17 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
internal interface IScreenhack : IDisposable
{
void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds);
void SetGraphicsDevice(GraphicsDevice graphicsDevice, Rectangle presentationParametersBounds);
}
}

View File

@ -0,0 +1,193 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
internal abstract class Processing
{
protected Random rng;
protected int Random(int max)
{
if (rng == null)
rng = new Random();
return rng.Next(max);
}
protected float Random(float min, float max)
{
if (min >= max)
return min;
float diff = max - min;
return Random(diff) + min;
}
protected float Random(float max)
{
if (rng == null)
rng = new Random();
if (max == 0.0F)
return 0.0F;
float f = 0.0F;
while (true)
{
f = rng.NextSingle() * max;
if (f != max)
return f;
}
}
private bool stroke;
private float strokeR, strokeG, strokeB, strokeA;
private int strokeRi, strokeGi, strokeBi, strokeAi;
private int strokeColor;
private bool strokeAlpha;
protected void Stroke(float paramFloat1, float paramFloat2)
{
ColorCalc(paramFloat1, paramFloat2);
ColorStroke();
}
private void ColorStroke()
{
this.stroke = true;
this.strokeR = this.calcR;
this.strokeG = this.calcG;
this.strokeB = this.calcB;
this.strokeA = this.calcA;
this.strokeRi = this.calcRi;
this.strokeGi = this.calcGi;
this.strokeBi = this.calcBi;
this.strokeAi = this.calcAi;
this.strokeColor = this.calcColor;
this.strokeAlpha = this.calcAlpha;
this.strokeColorObject = new Color();
this.strokeColorObject.A = (byte)this.strokeAi;
this.strokeColorObject.R = (byte)this.strokeRi;
this.strokeColorObject.G = (byte)this.strokeGi;
this.strokeColorObject.B = (byte)this.strokeBi;
}
private float colorModeX, colorModeA, calcR, calcG, calcB, calcA;
private bool colorScale;
private int calcRi, calcGi, calcBi, calcAi, calcColor;
private bool calcAlpha;
protected Color strokeColorObject;
protected int colorMode = 1;
private float colorModeY, colorModeZ;
protected void ColorCalc(float paramFloat1, float paramFloat2)
{
this.calcR = this.colorScale ? (paramFloat1 / this.colorModeX) : paramFloat1;
this.calcG = this.calcR;
this.calcB = this.calcR;
this.calcA = this.colorScale ? (paramFloat2 / this.colorModeA) : paramFloat2;
this.calcRi = (int)(this.calcR);
this.calcGi = (int)(this.calcG);
this.calcBi = (int)(this.calcB);
this.calcAi = (int)(this.calcA);
this.calcColor = this.calcAi << 24 | this.calcRi << 16 | this.calcGi << 8 | this.calcBi;
if (this.calcAi != 255)
this.calcAlpha = false;
this.calcAlpha = true;
}
protected void Stroke(float r, float g, float b, float a)
{
ColorCalc(r, g, b, a);
ColorStroke();
}
protected void ColorCalc(float paramFloat1, float paramFloat2, float paramFloat3, float paramFloat4)
{
float f1, f2, f3, f4, f5;
switch (this.colorMode)
{
case 1:
if (this.colorScale)
{
this.calcR = paramFloat1 / this.colorModeX;
this.calcG = paramFloat2 / this.colorModeY;
this.calcB = paramFloat3 / this.colorModeZ;
this.calcA = paramFloat4 / this.colorModeA;
break;
}
this.calcR = paramFloat1;
this.calcG = paramFloat2;
this.calcB = paramFloat3;
this.calcA = paramFloat4;
break;
case 3:
paramFloat1 /= this.colorModeX;
paramFloat2 /= this.colorModeY;
paramFloat3 /= this.colorModeZ;
this.calcA = this.colorScale ? (paramFloat4 / this.colorModeA) : paramFloat4;
if (paramFloat2 == 0.0F)
{
this.calcR = this.calcG = this.calcB = paramFloat3;
break;
}
f1 = (paramFloat1 - (int)paramFloat1) * 6.0F;
f2 = f1 - (int)f1;
f3 = paramFloat3 * (1.0F - paramFloat2);
f4 = paramFloat3 * (1.0F - paramFloat2 * f2);
f5 = paramFloat3 * (1.0F - paramFloat2 * (1.0F - f2));
switch ((int)f1)
{
case 0:
this.calcR = paramFloat3;
this.calcG = f5;
this.calcB = f3;
break;
case 1:
this.calcR = f4;
this.calcG = paramFloat3;
this.calcB = f3;
break;
case 2:
this.calcR = f3;
this.calcG = paramFloat3;
this.calcB = f5;
break;
case 3:
this.calcR = f3;
this.calcG = f4;
this.calcB = paramFloat3;
break;
case 4:
this.calcR = f5;
this.calcG = f3;
this.calcB = paramFloat3;
break;
case 5:
this.calcR = paramFloat3;
this.calcG = f3;
this.calcB = f4;
break;
}
break;
}
this.calcRi = (int)(1.0f * this.calcR);
this.calcGi = (int)(1.0f * this.calcG);
this.calcBi = (int)(1.0f * this.calcB);
this.calcAi = (int)(1.0f * this.calcA);
this.calcColor = this.calcAi << 24 | this.calcRi << 16 | this.calcGi << 8 | this.calcBi;
if (this.calcAi != 255)
this.calcAlpha = false;
this.calcAlpha = true;
}
protected abstract void Point(float x, float y);
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
public class ScreenhackException : Exception
{
public ScreenhackException()
{
}
public ScreenhackException(string message) : base(message)
{
}
public ScreenhackException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ImGuiNET.SampleProgram.XNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using skyscraper5.Skyscraper.Plugins;
namespace skyscraper8.UI.ImGui.MonoGame.Screenhacks
{
internal class ScreenhackManager : GameObject
{
private Dictionary<int, ConstructorInfo> knownScreenhacks;
public ScreenhackManager()
{
knownScreenhacks = new Dictionary<int, ConstructorInfo>();
Assembly assembly = GetType().Assembly;
Type screenhackBaseType = typeof(IScreenhack);
Type pluginPriorityTYpe = typeof(PluginPriorityAttribute);
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (!type.IsAssignableTo(screenhackBaseType))
continue;
if (type.IsInterface)
continue;
ConstructorInfo constructor = type.GetConstructor(new Type[] { });
Attribute attribute = type.GetCustomAttributes(pluginPriorityTYpe).FirstOrDefault();
if (attribute == null)
{
throw new ScreenhackException(String.Format("{0} is missing the {1} attribute.", type.Name, nameof(PluginPriorityAttribute)));
}
PluginPriorityAttribute ppa = (PluginPriorityAttribute)attribute;
knownScreenhacks.Add(ppa.Priority, constructor);
}
}
private IScreenhack currentScreenhack;
private int currentScreenhackId;
public int WantedScreenhackId { get; set; }
public void Update(GameTime gameTime, GraphicsDevice graphicsDevice)
{
if (currentScreenhack == null || currentScreenhackId != WantedScreenhackId)
{
ConstructorInfo constructorInfo = knownScreenhacks[WantedScreenhackId];
object invoke = constructorInfo.Invoke(new object[] { });
currentScreenhack = (IScreenhack)invoke;
currentScreenhack.SetGraphicsDevice(graphicsDevice,graphicsDevice.PresentationParameters.Bounds);
currentScreenhackId = WantedScreenhackId;
}
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle windowBounds)
{
currentScreenhack.Draw(gameTime, spriteBatch, windowBounds);
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImGuiNET;
using ImGuiNET.SampleProgram.XNA;
using Microsoft.Xna.Framework;
using skyscraper8.UI.ImGui.MonoGame.Screenhacks;
namespace moe.yo3explorer.skyscraper8.UI.MonoGame
{
internal class SkyscraperGame : BaseGame
{
private ScreenhackManager screenhackManager;
protected override void LoadContent()
{
screenhackManager = new ScreenhackManager();
screenhackManager.WantedScreenhackId = 1;
_gameObjects.Add(screenhackManager);
base.LoadContent();
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.White);
base.Draw(gameTime);
}
protected override void ImGuiLayout()
{
ImGui.BeginMainMenuBar();
if (ImGui.BeginMenu("Jobs"))
{
if (ImGui.MenuItem("Quit"))
{
Exit();
}
ImGui.EndMenu();
}
ImGui.EndMainMenuBar();
base.ImGuiLayout();
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\skyscraper8\skyscraper8.csproj" />
<ProjectReference Include="..\skyscraper8.UI.ImGui.MonoGame.Bridge\skyscraper8.UI.ImGui.MonoGame.Bridge.csproj" />
</ItemGroup>
</Project>

View File

@ -464,7 +464,7 @@ namespace SkyscraperUI
if (showMainMenu)
{
ImGui.BeginMainMenuBar();
ImGui.BeginMainMenuBar();
if (ImGui.BeginMenu("Jobs"))
{
if (ImGui.MenuItem("Connect to TCP/IP Stream", CanScrapeFromTcpIp()))

View File

@ -57,12 +57,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UIs", "UIs", "{E23457C5-3A3
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper5.UI.WindowsForms", "GUIs\skyscraper5.UI\skyscraper5.UI.WindowsForms.csproj", "{46CACA1C-F9B2-2FE0-2068-716F381325E9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.ImGui", "GUIs\skyscraper8.UI.ImGui\skyscraper8.UI.ImGui.csproj", "{BDBDB7A9-D0A4-9B89-0801-2935B2066551}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.ImGui.SDL2", "GUIs\skyscraper8.UI.ImGui\skyscraper8.UI.ImGui.SDL2.csproj", "{BDBDB7A9-D0A4-9B89-0801-2935B2066551}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.EPGCollectorPort", "PrivateDataSpecifiers\skyscraper8.EPGCollectorPort\skyscraper8.EPGCollectorPort.csproj", "{CF21D250-9804-4191-89F5-95821E3AF39D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.AnagramViewer", "GUIs\skyscraper8.AnagramViewer\skyscraper8.AnagramViewer.csproj", "{EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.ImGui.MonoGame", "GUIs\skyscraper8.UI.ImGui.MonoGame\skyscraper8.UI.ImGui.MonoGame.csproj", "{839C3783-020F-77D6-C22C-D50DCC97CDD6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.ImGui.MonoGame.Bridge", "GUIs\skyscraper8.UI.ImGui.MonoGame.Bridge\skyscraper8.UI.ImGui.MonoGame.Bridge.csproj", "{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -157,6 +161,14 @@ Global
{EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Release|Any CPU.Build.0 = Release|Any CPU
{839C3783-020F-77D6-C22C-D50DCC97CDD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{839C3783-020F-77D6-C22C-D50DCC97CDD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{839C3783-020F-77D6-C22C-D50DCC97CDD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{839C3783-020F-77D6-C22C-D50DCC97CDD6}.Release|Any CPU.Build.0 = Release|Any CPU
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -182,6 +194,8 @@ Global
{BDBDB7A9-D0A4-9B89-0801-2935B2066551} = {E23457C5-3A34-48EE-8107-C91E2C174B2D}
{CF21D250-9804-4191-89F5-95821E3AF39D} = {56729C39-B90E-4DF3-A557-DB93436FB5FF}
{EBB6B1CF-2597-4962-AA31-2B42B4C28C7D} = {E23457C5-3A34-48EE-8107-C91E2C174B2D}
{839C3783-020F-77D6-C22C-D50DCC97CDD6} = {E23457C5-3A34-48EE-8107-C91E2C174B2D}
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2} = {E23457C5-3A34-48EE-8107-C91E2C174B2D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5147EFA3-3D4E-4FDE-8A36-5840E8F1B80E}

View File

@ -12,28 +12,20 @@ using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.RecordingImporter;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.FrameGrabber;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper5.Skyscraper.Webserver;
using skyscraper5.src.Aac;
using skyscraper5.src.Mpeg2.PacketFilter;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper5.T2MI;
using skyscraper8.Skyscraper.IO;
using skyscraper8.Skyscraper.Scraper.Storage;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using skyscraper5.Dvb.Descriptors;
using skyscraper8.Skyscraper.Drawing;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]
namespace skyscraper5
@ -43,6 +35,12 @@ namespace skyscraper5
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static void IntegrationTest()
{
FileStream fileStream = File.OpenRead("C:\\Temp\\ule-iww.ts");
TsContext tesContext = new TsContext();
SkyscraperContext skyscraperContext = new SkyscraperContext(tesContext, new InMemoryScraperStorage(), new NullObjectStorage());
skyscraperContext.InitalizeFilterChain();
tesContext.RegisterPacketProcessor(0x666, new StreamTypeAutodetection(0x666, skyscraperContext));
skyscraperContext.IngestFromStream(fileStream);
}
static void Main(string[] args)