Files
EmilandClaude Sonnet 5 0c2547a804 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
2026-09-02 16:10:35 +03:00

24 lines
691 B
C#

using System.Numerics;
using Engine.Render.Contracts;
using Engine.Windowing.Contracts;
namespace Engine.Render;
internal sealed class CameraService(IEngineWindow window) : ICameraService
{
public Vector3 Position { get; set; } = new(0f, 3f, 6f);
public Vector3 Target { get; set; } = Vector3.Zero;
public Matrix4x4 View => Matrix4x4.CreateLookAt(Position, Target, Vector3.UnitY);
public Matrix4x4 Projection
{
get
{
var size = window.Native.FramebufferSize;
var aspect = size.Y == 0 ? 1f : (float)size.X / size.Y;
return Matrix4x4.CreatePerspectiveFieldOfView(MathF.PI / 4f, aspect, 0.1f, 100f);
}
}
}