Add real 3D rendering pipeline (camera, view/projection, QuadRenderer)

Replaces M2's single hardcoded NDC-space triangle with a real perspective
pipeline: ICameraService/CameraService (fixed camera at (0,3,6) looking at
the origin), a QuadRenderer marker component, and a Draw() that iterates
world.Query<QuadRenderer>() to draw every such GameObject at its own
WorldMatrix. Needed as the foundation for M3's gizmos, which have to map
a screen-space drag onto a real 3D axis — a 2D quad and no camera can't
support that.

Two real bugs found and fixed empirically, not designed in from the start:

- SystemAccessScope correctly threw on Draw() querying QuadRenderer without
  declaring Reads<QuadRenderer>() on its Schedule.Add registration — the
  safety net catching a real omission, exactly as designed.
- UniformMatrix4 needed transpose:true, not false. System.Numerics.Matrix4x4
  is row-major in memory; glUniformMatrix4fv with transpose=GL_FALSE reads
  that layout as column-major instead. With transpose=false the quad simply
  didn't render — no error, no crash, just a blank screen. Confirmed via a
  correctly perspective-foreshortened, texture-mapped quad after the fix.

Also adds samples/WindowDemo/scene.json (a single Quad GameObject with a
QuadRenderer) so the new pipeline has something real to draw, loaded via
the existing --scene flag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N
This commit is contained in:
Emil
2026-09-02 16:10:35 +03:00
co-authored by Claude Sonnet 5
parent 2f5a8bfde6
commit 0c2547a804
6 changed files with 153 additions and 27 deletions
@@ -1,3 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\..\src\Engine.Kernel\Engine.Kernel.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,25 @@
using System.Numerics;
namespace Engine.Render.Contracts;
/// <summary>
/// The one camera engine.render draws through — read for gizmo/ray-picking
/// math (editor.shell needs View/Projection to turn a 2D mouse position
/// into a 3D ray), settable for camera controls (orbit, fly) that don't
/// exist yet. Not a Component/GameObject-driven multi-camera system —
/// there's exactly one camera and nothing needs more than that yet; see
/// the note on RenderPlugin for why that's a deliberate scope line, not
/// an oversight.
/// </summary>
public interface ICameraService
{
Vector3 Position { get; set; }
Vector3 Target { get; set; }
Matrix4x4 View { get; }
/// <summary>Recomputed against the window's current size on every
/// read — a cached value would go stale the moment the window
/// resizes.</summary>
Matrix4x4 Projection { get; }
}
@@ -0,0 +1,18 @@
using Engine.Kernel.World;
namespace Engine.Render.Contracts;
/// <summary>
/// Marks a GameObject as a flat, unit-sized (1x1 world unit before
/// scaling) quad, drawn at its own WorldMatrix — the only renderable
/// component that exists yet. No size fields here: Transform.LocalScale
/// already means "how big," and a second, competing way to say the same
/// thing would just raise the question of which one wins. A real
/// mesh/material system (arbitrary geometry, per-instance textures) is
/// real content-pipeline work, out of scope here — this exists so M3's
/// gizmos have something real to select and move, not to be a renderer.
/// Every QuadRenderer shares engine.render's one hardcoded texture for
/// the same reason TexturePath itself is hardcoded — there's no
/// material/asset-reference system yet to point it at one per instance.
/// </summary>
public sealed class QuadRenderer : Component;