Bootstrap Stride-hosted engine shell

This commit is contained in:
Meta Vens 2026-05-08 00:32:51 +01:00
commit 97c697b638
11 changed files with 229 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
bin/
obj/
artifacts/
*.user
*.suo
.vs/
.idea/

5
Aquarium.Engine.slnx Normal file
View file

@ -0,0 +1,5 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/Aquarium.Engine/Aquarium.Engine.csproj" />
</Folder>
</Solution>

View file

@ -30,3 +30,20 @@ Aquarium-first rather than framework-first.
Create the smallest C# executable that opens a window, owns the camera/Grid Create the smallest C# executable that opens a window, owns the camera/Grid
invariant, and renders one sun plus orbiting planets with diegetic labels. invariant, and renders one sun plus orbiting planets with diegetic labels.
Everything else earns its way back in. Everything else earns its way back in.
## Run
```powershell
dotnet build Aquarium.Engine.slnx
dotnet run --project src\Aquarium.Engine\Aquarium.Engine.csproj
```
Headless smoke:
```powershell
dotnet run --project src\Aquarium.Engine\Aquarium.Engine.csproj -- --headless
```
The first cut uses Stride as a NuGet-hosted shell through
`Stride.CommunityToolkit.Windows`. Stride is scaffolding; the Aquarium owns the
camera/Grid invariant and the renderer direction. See `docs/stride-boundary.md`.

38
docs/stride-boundary.md Normal file
View file

@ -0,0 +1,38 @@
# Stride Boundary
Stride enters Aquarium as a package dependency, not as vendored engine source.
## What Stride Provides
- Window/device/input scaffolding.
- Game loop hosting.
- Asset and graphics infrastructure where it helps.
- A place to insert Aquarium-owned renderer passes.
## What Aquarium Owns
- Camera/Grid invariants.
- World model and simulation state.
- CultNet and CultCache boundaries.
- Renderer architecture and field ownership.
- Diegetic UI grammar.
- Debug command surface.
## First Invariant
The camera target is a point on global XY. The Grid center is that same point.
Grid radius is derived from camera zoom distance, not from camera pitch/yaw or
screen projection. Orbiting the camera must not resize the Grid.
Current code expresses this through `OrbitCameraRig` and `GridFrame`.
## Current Warnings
`Stride.CommunityToolkit.Windows` brings Stride's asset/compiler toolchain into
the restore graph. The first restore/build currently reports transitive NuGet
advisories for `Microsoft.Build.Tasks.Core`, `NuGet.Packaging`, and
`NuGet.Protocol`, plus Stride's default missing `GameSettings` warning. These
are dependency/tooling warnings, not Aquarium code warnings.
Do not hide them globally yet. Track whether newer Stride/Toolkit packages clear
the advisories before adding warning suppressions.

6
global.json Normal file
View file

@ -0,0 +1,6 @@
{
"sdk": {
"version": "10.0.202",
"rollForward": "latestFeature"
}
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Stride.CommunityToolkit.Windows" Version="1.0.0-preview.62" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,26 @@
using Stride.CommunityToolkit.Engine;
using Stride.Engine;
using Stride.Games;
namespace Aquarium.Engine;
public static class AquariumHost
{
public static int Run(string[] args)
{
using var game = new Game();
var runtime = new AquariumRuntime(new AquariumRuntimeOptions(ParseHeadless(args)));
game.Run(
context: null,
start: runtime.Start,
update: runtime.Update);
return 0;
}
private static bool ParseHeadless(IEnumerable<string> args)
{
return args.Any(arg => string.Equals(arg, "--headless", StringComparison.OrdinalIgnoreCase));
}
}

View file

@ -0,0 +1,49 @@
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Games;
namespace Aquarium.Engine;
public sealed class AquariumRuntime
{
private readonly AquariumRuntimeOptions options;
private readonly OrbitCameraRig cameraRig;
private GridFrame gridFrame;
public AquariumRuntime(AquariumRuntimeOptions options)
{
this.options = options;
cameraRig = new OrbitCameraRig(
target: Vector2.Zero,
yawRadians: MathUtil.DegreesToRadians(35.0f),
pitchRadians: MathUtil.DegreesToRadians(42.0f),
distance: 24.0f);
gridFrame = GridFrame.FromCamera(cameraRig);
}
public void Start(Scene rootScene)
{
_ = rootScene;
Console.WriteLine("Aquarium Engine booted.");
Console.WriteLine($"Grid center: {gridFrame.Center}, radius: {gridFrame.Radius:0.00}");
Console.WriteLine("Stride is present as the host shell; Aquarium owns the invariants.");
}
public void Update(Scene rootScene, GameTime gameTime)
{
_ = rootScene;
_ = gameTime;
gridFrame = GridFrame.FromCamera(cameraRig);
if (options.Headless)
{
Console.WriteLine("Headless smoke reached first update.");
Environment.ExitCode = 0;
Environment.Exit(0);
}
}
}
public readonly record struct AquariumRuntimeOptions(bool Headless);

View file

@ -0,0 +1,3 @@
using Aquarium.Engine;
return AquariumHost.Run(args);

View file

@ -0,0 +1,11 @@
using Stride.Core.Mathematics;
namespace Aquarium.Engine;
public readonly record struct GridFrame(Vector2 Center, float Radius)
{
public static GridFrame FromCamera(OrbitCameraRig camera)
{
return new GridFrame(camera.Target, camera.Distance);
}
}

View file

@ -0,0 +1,53 @@
using Stride.Core.Mathematics;
namespace Aquarium.Engine;
public sealed class OrbitCameraRig
{
public OrbitCameraRig(Vector2 target, float yawRadians, float pitchRadians, float distance)
{
Target = target;
YawRadians = yawRadians;
PitchRadians = pitchRadians;
Distance = MathF.Max(1.0f, distance);
}
public Vector2 Target { get; private set; }
public float YawRadians { get; private set; }
public float PitchRadians { get; private set; }
public float Distance { get; private set; }
public Vector3 Position
{
get
{
var horizontal = Distance * MathF.Cos(PitchRadians);
var x = Target.X + horizontal * MathF.Cos(YawRadians);
var y = Target.Y + horizontal * MathF.Sin(YawRadians);
var z = Distance * MathF.Sin(PitchRadians);
return new Vector3(x, y, z);
}
}
public void Orbit(float yawDeltaRadians, float pitchDeltaRadians)
{
YawRadians += yawDeltaRadians;
PitchRadians = MathUtil.Clamp(
PitchRadians + pitchDeltaRadians,
MathUtil.DegreesToRadians(5.0f),
MathUtil.DegreesToRadians(85.0f));
}
public void Zoom(float wheelSteps)
{
Distance = MathF.Max(1.0f, Distance * MathF.Pow(0.92f, wheelSteps));
}
public void Pan(Vector2 gridDelta)
{
Target += gridDelta;
}
}