diff --git a/DemoUtilities/Window.cs b/DemoUtilities/Window.cs index 1cacc475b..3867e47eb 100644 --- a/DemoUtilities/Window.cs +++ b/DemoUtilities/Window.cs @@ -1,9 +1,9 @@ -using System; -using System.Diagnostics; +using BepuUtilities; using OpenTK; -using BepuUtilities; using OpenTK.Graphics; using OpenTK.Platform; +using System; +using System.Diagnostics; using System.Threading; using Vector2 = System.Numerics.Vector2; @@ -182,6 +182,39 @@ public void Run(Action updateHandler, Action onResize) windowUpdateLoopRunning = false; } + public void SingleFrame(Action updateHandler, Action onResize, float dt) + { + + if (disposed) + return; + if (resized) + { + //Note that minimizing or resizing the window to invalid sizes don't result in actual resize attempts. Zero width rendering surfaces aren't allowed. + if (window.Width > 0 && window.Height > 0) + { + onResize(new Int2(window.Width, window.Height)); + } + resized = false; + } + window.ProcessEvents(); + if (tryToClose) + { + window.Close(); + return; + } + long time = Stopwatch.GetTimestamp(); + + if (window.WindowState != WindowState.Minimized) + { + updateHandler(dt); + } + else + { + //If the window is minimized, take a breather. + Thread.Sleep(1); + } + } + private bool disposed; public void Dispose() { diff --git a/Demos.GL.sln b/Demos.GL.sln index ed964e8a3..653cf22f7 100644 --- a/Demos.GL.sln +++ b/Demos.GL.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31423.177 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11123.170 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoContentLoader", "DemoContentLoader\DemoContentLoader.csproj", "{FABD2BE3-697B-4B57-85D0-1077A3198C5C}" EndProject @@ -17,6 +17,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demos", "Demos.GL\Demos.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoRenderer", "DemoRenderer.GL\DemoRenderer.csproj", "{85C39598-1A63-4944-B619-25F3CD76C7A2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fulfil.Libs", "..\Fulfil.NET\Fulfil.Libs\Fulfil.Libs.csproj", "{6920EBDC-AA79-7F88-C070-0FC01286F971}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fulfil.Tests", "..\Fulfil.NET\Fulfil.Tests\Fulfil.Tests.csproj", "{D4002212-2B11-42C5-9C1A-4539915EAB0C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -201,6 +205,54 @@ Global {85C39598-1A63-4944-B619-25F3CD76C7A2}.ReleaseNoProfiling|x64.Build.0 = Release|Any CPU {85C39598-1A63-4944-B619-25F3CD76C7A2}.ReleaseNoProfiling|x86.ActiveCfg = Release|Any CPU {85C39598-1A63-4944-B619-25F3CD76C7A2}.ReleaseNoProfiling|x86.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|ARM.ActiveCfg = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|ARM.Build.0 = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|x64.ActiveCfg = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|x64.Build.0 = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|x86.ActiveCfg = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Debug|x86.Build.0 = Debug|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|Any CPU.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|ARM.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|ARM.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|x64.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|x64.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|x86.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.Release|x86.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|Any CPU.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|Any CPU.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|ARM.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|ARM.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|x64.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|x64.Build.0 = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|x86.ActiveCfg = Release|Any CPU + {6920EBDC-AA79-7F88-C070-0FC01286F971}.ReleaseNoProfiling|x86.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|ARM.Build.0 = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|x64.ActiveCfg = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|x64.Build.0 = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Debug|x86.Build.0 = Debug|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|Any CPU.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|ARM.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|ARM.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|x64.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|x64.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|x86.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.Release|x86.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|Any CPU.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|Any CPU.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|ARM.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|ARM.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|x64.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|x64.Build.0 = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|x86.ActiveCfg = Release|Any CPU + {D4002212-2B11-42C5-9C1A-4539915EAB0C}.ReleaseNoProfiling|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Demos.GL/Broken/CompoundFallsThroughFloor.cs b/Demos.GL/Broken/CompoundFallsThroughFloor.cs new file mode 100644 index 000000000..3d3f4e7bc --- /dev/null +++ b/Demos.GL/Broken/CompoundFallsThroughFloor.cs @@ -0,0 +1,71 @@ + +using BepuPhysics; +using BepuPhysics.Collidables; +using BepuUtilities; +using DemoContentLoader; +using DemoRenderer; +using DemoUtilities; +using System.Numerics; + +namespace Demos.Broken; + +internal class CompoundFallsThroughFloor : Demo +{ + private BodyActivityDescription NeverSleep() => new BodyActivityDescription(sleepThreshold: float.MinValue, minimumTimestepCountUnderThreshold: byte.MaxValue); + + public override void Initialize(ContentArchive content, Camera camera) + { + camera.Position = new Vector3(-1000f, 400, -1000f); + camera.Yaw = MathHelper.Pi * 3f / 4; + camera.Pitch = MathHelper.Pi * 0.1f; + + var narrowPhaseCallbacks = new SimpleNarrowPhaseCallbacks( + frictionCoefficient: 0.3f, + maximumRecoveryVelocity: 500, + impactFrequency: 20, + impactDampingRatio: 0.5f + ); + + var gravity = new Vector3(0, -9807f, 0); // mm/s + var timeStepSeconds = .12f; + + Simulation = Simulation.Create(BufferPool, narrowPhaseCallbacks, new PoseIntegratorCallbacks(gravity: gravity, timeStepSeconds: timeStepSeconds), new SolveDescription(8, 1)); + var floor = new Box(1000, 1, 1000); + + Simulation.Statics.Add(new StaticDescription(RigidPose.Identity, Simulation.Shapes.Add(floor))); + + + // shared constants + const float itemMass = 1; + const float startingHeight = 18; + + // Item as a standalone (not compound) + { + var rawCollidableBox = new Box(100, 130, 100); + var boxPose = new RigidPose(new Vector3(-300, rawCollidableBox.HalfHeight + floor.HalfHeight + startingHeight, 300), Quaternion.Identity); + var boxInertia = rawCollidableBox.ComputeInertia(itemMass); + var boxIndex = Simulation.Shapes.Add(rawCollidableBox); + var collisionDetection = new ContinuousDetection(); + var boxCollidable = new CollidableDescription(boxIndex, collisionDetection); + var boxDescription = BodyDescription.CreateDynamic(boxPose, boxInertia, boxCollidable, NeverSleep()); + Simulation.Bodies.Add(boxDescription); + } + + // Item as compound + { + var childShape = new Box(100, 130, 100); + var compoundPose = new RigidPose(new Vector3(300, childShape.HalfHeight + floor.HalfHeight + startingHeight, -300), Quaternion.Identity); + var builder = new CompoundBuilder(BufferPool, Simulation.Shapes, 1); + + builder.Add(childShape, RigidPose.Identity, itemMass); + builder.BuildDynamicCompound(out var children, out var inertia); + Simulation.Bodies.Add(BodyDescription.CreateDynamic(compoundPose, inertia, Simulation.Shapes.Add(new Compound(children)), NeverSleep())); + } + + } + + public override void Update(Window window, Camera camera, Input input, float dt) + { + base.Update(window, camera, input, dt); + } +} diff --git a/Demos.GL/Broken/PoseIntegratorCallbacks.cs b/Demos.GL/Broken/PoseIntegratorCallbacks.cs new file mode 100644 index 000000000..d14fcf7eb --- /dev/null +++ b/Demos.GL/Broken/PoseIntegratorCallbacks.cs @@ -0,0 +1,33 @@ +using BepuPhysics; +using BepuUtilities; +using System.Numerics; + +namespace Demos.Broken; + +/// +/// An implementation of INarrowPhaseCallbacks (a BePu interface) needed to get simulation running with BePu. +/// +/// Basically a verbatim copy from the example given here: https://github.com/bepu/bepuphysics2/blob/master/Demos/Demos/SimpleSelfContainedDemo.cs +public struct PoseIntegratorCallbacks : IPoseIntegratorCallbacks +{ + public void Initialize(Simulation simulation) { } + + public readonly AngularIntegrationMode AngularIntegrationMode => AngularIntegrationMode.Nonconserving; + + public readonly bool AllowSubstepsForUnconstrainedBodies => false; + + public readonly bool IntegrateVelocityForKinematics => false; + + private readonly Vector3Wide _gravityWideDt; + public PoseIntegratorCallbacks(Vector3 gravity, float timeStepSeconds) : this() + { + _gravityWideDt = Vector3Wide.Broadcast(gravity * timeStepSeconds); + } + + public void IntegrateVelocity(Vector bodyIndices, Vector3Wide position, QuaternionWide orientation, BodyInertiaWide localInertia, Vector integrationMask, int workerIndex, Vector dt, ref BodyVelocityWide velocity) + { + velocity.Linear += _gravityWideDt; + } + + public void PrepareForIntegration(float dt) { } +} diff --git a/Demos.GL/Broken/SimpleNarrowPhaseCallbacks.cs b/Demos.GL/Broken/SimpleNarrowPhaseCallbacks.cs new file mode 100644 index 000000000..0d129de3e --- /dev/null +++ b/Demos.GL/Broken/SimpleNarrowPhaseCallbacks.cs @@ -0,0 +1,52 @@ +using BepuPhysics; +using BepuPhysics.Collidables; +using BepuPhysics.CollisionDetection; +using BepuPhysics.Constraints; + +namespace Demos.Broken; + +/// +/// An implementation of INarrowPhaseCallbacks (a BePu interface) needed to get simulation running with BePu. +/// +/// Roughly a verbatim copy of the example given here: https://github.com/bepu/bepuphysics2/blob/master/Demos/Demos/SimpleSelfContainedDemo.cs +public struct SimpleNarrowPhaseCallbacks : INarrowPhaseCallbacks +{ + private readonly float _frictionCoefficient; + private readonly float _maximumRecoveryVelocity; + private readonly float _impactFrequency; + private readonly float _impactDampingRatio; + + //TODO (Nigel): verify that these populate in the online docs without the summary tag + + /// Coefficient of friction to apply for the constraint. Maximum friction force will be equal to the normal force times the friction coefficient. + /// Maximum relative velocity along the contact normal at which the collision constraint will recover from penetration. Clamps the velocity goal created from the spring settings. + /// Target number of undamped oscillations per unit of time. + /// Ratio of the spring's actual damping to its critical damping. 0 is undamped, 1 is critically damped, and higher values are overdamped. + public SimpleNarrowPhaseCallbacks(float frictionCoefficient, float maximumRecoveryVelocity, float impactFrequency, float impactDampingRatio) + { + _frictionCoefficient = frictionCoefficient; + _maximumRecoveryVelocity = maximumRecoveryVelocity; + _impactFrequency = impactFrequency; + _impactDampingRatio = impactDampingRatio; + } + public void Initialize(Simulation simulation) { } + + public void Dispose() { } + + public bool AllowContactGeneration(int workerIndex, CollidablePair pair, int childIndexA, int childIndexB) => true; + public bool ConfigureContactManifold(int workerIndex, CollidablePair pair, ref TManifold manifold, out PairMaterialProperties pairMaterial) where TManifold : unmanaged, IContactManifold + { + pairMaterial.FrictionCoefficient = _frictionCoefficient; + pairMaterial.MaximumRecoveryVelocity = _maximumRecoveryVelocity; + pairMaterial.SpringSettings = new SpringSettings(_impactFrequency, _impactDampingRatio); + //For the purposes of the demo, contact constraints are always generated. + return true; + } + public bool ConfigureContactManifold(int workerIndex, CollidablePair pair, int childIndexA, int childIndexB, ref ConvexContactManifold manifold) => true; + + public bool AllowContactGeneration(int workerIndex, CollidableReference a, CollidableReference b, ref float speculativeMargin) + { + + return a.Mobility == CollidableMobility.Dynamic || b.Mobility == CollidableMobility.Dynamic; + } +} diff --git a/Demos.GL/Demos.csproj b/Demos.GL/Demos.csproj index f4d9b1c9b..44583cbf7 100755 --- a/Demos.GL/Demos.csproj +++ b/Demos.GL/Demos.csproj @@ -33,6 +33,8 @@ + + diff --git a/Demos.GL/FC/FcDemo.cs b/Demos.GL/FC/FcDemo.cs new file mode 100644 index 000000000..3a9d971ad --- /dev/null +++ b/Demos.GL/FC/FcDemo.cs @@ -0,0 +1,46 @@ +using BepuUtilities; +using DemoContentLoader; +using DemoRenderer; +using Fulfil.Libs.Machines.Mars.Dispense.SideDispenseDropTargeting.DTO; +using Fulfil.Libs.Machines.Mars.Dispense.SideDispenseDropTargeting.Physics; +using Fulfil.Tests.Libraries.Machines.Mars.Dispense.SideDispenseDropTargeting.Mocks; +using Fulfil.Tests.TestObjects; +using System.Numerics; + +namespace Demos.FC; + +internal class FcDemo : Demo +{ + public override void Initialize(ContentArchive content, Camera camera) + { + camera.Position = new Vector3(100, 150, 300); + camera.Yaw = 0; + camera.Pitch = MathHelper.Pi * 0.1f; + + var startingHeight = 30; + const double foamThickness = 9.5; + var itemDims = new RectangularPrism(100, 130, 100); + var itemDrop = MockItemGenerator.Build(itemDimensions: itemDims); + var dropContext = MockDropContextBuilder.Build(item: itemDrop); + var bagDims = dropContext.PhysicalDropConditions.SystemPhysicalProperties.BagCavityDimensions; + var itemLocation = new Transform(Vector3Helpers.FromDoubles(x: bagDims.XLengthMm / 2, y: startingHeight + foamThickness, z: -bagDims.ZLengthMm / 2)); + var physicsEvaluator = new BePuPhysicsSimulator(new IgnoreTheReleasePoint(itemLocation)) + { + TimeStepMs = 120, + EvaluationTimeMs = 2500, + FrictionCoefficient = 0.3f, + MaximumRecoveryVelocity = 50, + ImpactFrequency = 20, + ImpactDampingRatio = 0.5f, + DispenseArmRetractionSpeedMmPerSecond = 500, + DispenseArmConveyorSpeedMMs = 0, + DispenseArmRemovalTimeMs = 2500, + StartingDistanceBehindTippingPointMm = 0, + StoppedDistanceMm = 0.5, + StoppedRotaionDegrees = 0.5, + StoppedFrames = 3, + }; + var simSetup = physicsEvaluator.InitializeSimulation(new Vector3(0, 0, 0), dropContext, BufferPool); + Simulation = simSetup.Simulation; + } +} diff --git a/Demos/Demo.cs b/Demos/Demo.cs index 329db2071..be2d6506e 100644 --- a/Demos/Demo.cs +++ b/Demos/Demo.cs @@ -1,11 +1,11 @@ -using BepuUtilities.Memory; +using BepuPhysics; +using BepuUtilities; +using BepuUtilities.Memory; +using DemoContentLoader; using DemoRenderer; +using DemoRenderer.UI; using DemoUtilities; -using BepuPhysics; using System; -using DemoRenderer.UI; -using DemoContentLoader; -using BepuUtilities; namespace Demos; @@ -62,7 +62,7 @@ public virtual void Update(Window window, Camera camera, Input input, float dt) //fully decouple simulation and rendering rates across different threads. //(In either case, you'd also want to interpolate or extrapolate simulation results during rendering for smoothness.) //Note that taking steps of variable length can reduce stability. Gradual or one-off changes can work reasonably well. - Simulation.Timestep(TimestepDuration, ThreadDispatcher); + Simulation.Timestep(dt, ThreadDispatcher); ////Here's an example of how it would look to use more frequent updates, but still with a fixed amount of time simulated per update call: //const float timeToSimulate = 1 / 60f; diff --git a/Demos/DemoSet.cs b/Demos/DemoSet.cs index e2b78ac67..3eb7059f9 100644 --- a/Demos/DemoSet.cs +++ b/Demos/DemoSet.cs @@ -1,12 +1,13 @@ using DemoContentLoader; using DemoRenderer; +using Demos.Broken; using Demos.Demos; using Demos.Demos.Cars; using Demos.Demos.Characters; using Demos.Demos.Dancers; using Demos.Demos.Sponsors; using Demos.Demos.Tanks; -using Demos.SpecializedTests; +using Demos.FC; using System; using System.Collections.Generic; @@ -44,6 +45,9 @@ struct Option public DemoSet() { + + AddOption(); + AddOption(); AddOption(); AddOption(); AddOption(); diff --git a/Demos/GameLoop.cs b/Demos/GameLoop.cs index ddedab484..8e0435fab 100644 --- a/Demos/GameLoop.cs +++ b/Demos/GameLoop.cs @@ -1,8 +1,8 @@ -using DemoRenderer; +using BepuUtilities; +using BepuUtilities.Memory; +using DemoRenderer; using DemoUtilities; using System; -using BepuUtilities; -using BepuUtilities.Memory; namespace Demos; @@ -29,10 +29,10 @@ public GameLoop(Window window) window.Resolution, enableDeviceDebugLayer: false ); Renderer = new Renderer(Surface); - Camera = new Camera(window.Resolution.X / (float)window.Resolution.Y, (float)Math.PI / 3, 0.01f, 100000); + Camera = new Camera(window.Resolution.X / (float)window.Resolution.Y, (float)Math.PI / 3, 0.01f, 100000); } - void Update(float dt) + public void Update(float dt) { Input.Start(); if (DemoHarness != null) @@ -53,6 +53,12 @@ public void Run(DemoHarness harness) Window.Run(Update, OnResize); } + public void SingleFrame(DemoHarness harness, float dt) + { + DemoHarness = harness; + Window.SingleFrame(Update, OnResize, dt); + } + private void OnResize(Int2 resolution) { //We just don't support true fullscreen in the demos. Would be pretty pointless. diff --git a/Demos/Program.cs b/Demos/Program.cs index aaa4f3ca5..e0551d406 100644 --- a/Demos/Program.cs +++ b/Demos/Program.cs @@ -2,12 +2,64 @@ using DemoContentLoader; using DemoUtilities; using OpenTK; +using System; +using System.Runtime.InteropServices; +using System.Threading; + + namespace Demos; + + class Program { + + [DllImport("user32.dll")] + static extern short GetAsyncKeyState(int key); + + static bool IsKeyPressed(int key) => (GetAsyncKeyState(key) & 0x8000) != 0; + + + + static void Main() + { + var window = new Window("pretty cool multicolored window", + new Int2((int)(DisplayDevice.Default.Width * 0.75f), (int)(DisplayDevice.Default.Height * 0.75f)), WindowMode.Windowed); + var loop = new GameLoop(window); + ContentArchive content; + using (var stream = typeof(Program).Assembly.GetManifestResourceStream("Demos.Demos.contentarchive")) + { + content = ContentArchive.Load(stream); + } + //HeadlessTest.Test(content, 4, 32, 512); + var demo = new DemoHarness(loop, content); + var dt = 0.12f; + loop.Update(dt); + DateTime? arrowDownTime = null; + var holdDownTimeMs = 500; + while (true) + { + if (IsKeyPressed(0x1B)) break; // escape + if (!IsKeyPressed(0x27)) + { + arrowDownTime = null; + Thread.Sleep(10); + continue; + } + if (arrowDownTime is null || (DateTime.Now - arrowDownTime.Value).TotalMilliseconds > holdDownTimeMs) + { + loop.SingleFrame(demo, dt); + Thread.Sleep(50); + } + } + + loop.Dispose(); + window.Dispose(); + } + + static void OldMain() { var window = new Window("pretty cool multicolored window", new Int2((int)(DisplayDevice.Default.Width * 0.75f), (int)(DisplayDevice.Default.Height * 0.75f)), WindowMode.Windowed);