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
invariant, and renders one sun plus orbiting planets with diegetic labels.
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;
}
}