faabfc2cb4eca95ced73c4e94731e91567098337
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2d0168104d |
M4: PhysicsDemo — the "one small game, end to end"
samples/PhysicsDemo ties physics, audio, input, and rendering together through nothing but the kernel's own vocabulary — no plugin here references another plugin's implementation, only Contracts. Press Space to drop a box; engine.physics simulates it falling onto a static ground; CubeRenderer (new — engine.render's QuadRenderer drew flat cards, no good for a physics demo where a BoxCollider needs to actually look like a box) draws it; physics-demo-game watches each spawned box's own Y velocity via IPhysicsService and plays a bounce sound via IAudioService the moment a real fall settles. A looping ambient track plays throughout via AudioSource's own PlayOnAwake. project.json deliberately excludes engine.editor — this is the shippable configuration M4's "done when" actually asks for, not the dev one. "Landed" isn't a Box3D contact event — the native shim never exposed one (nothing but scalars crosses that boundary, see lingua_physics.c). Watching velocity every frame is the honest, right-sized alternative for a demo this size, not a shortcut around missing infrastructure. Verified two ways. First, real Space-key input isn't simulable here (no xdotool/ydotool under this Wayland session) — so PhysicsDemoGame.Tests drives the actual PhysicsDemoGamePlugin.Configure/Tick through the real Schedule with a controllable fake IEngineInput (and fake IPhysicsService/ IAudioService, since engine.physics/engine.audio's own correctness is already covered elsewhere): spawn-on-press with edge detection, the Rigidbody/BoxCollider/CubeRenderer combo the spawned box actually gets, and — the case most likely to be subtly wrong — a box that never actually falls doesn't false-positive as "landed," only one that fell past FallingThreshold and then settled does, exactly once. All 5 passed immediately. Second, a real windowed run: a box placed in scene.json above the ground visibly falls and settles onto it on screen after a real few-second wait — screenshotting by --screenshot-after-frames alone turned out not to prove this (VSync is off, so frames race by far faster than real physics time passes; enough elapsed frames isn't enough elapsed seconds), an interactive run with a real sleep before the screenshot command is what actually shows it. Full suite: 92 tests. Remaining for M4: the Linux + Windows build pipeline. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N |
||
|
|
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 |
||
|
|
f6d9ea896f |
Close M1: real triangle, engine.input, screenshot-to-file capture
All three plugins M1 called for now exist over Silk.NET, and the milestone's actual claim is proven against a live GL context, not just argued: edit a plugin's code, rebuild just it, reload it while a real window stays open, see the change with no app restart. Verified directly this time — two PNGs of the same running window, before and after a live reload (orange triangle -> green, same process, same window, same GL context throughout) — not by analogy to the earlier clear-color version. engine.render, upgraded from clear-color to real geometry: - Vertex/fragment shaders compiled and linked at Configure() time, with real error checking (GetShader/GetProgram *Status + InfoLog on failure) rather than trusting hand-typed GLSL to just work. - VAO/VBO for a hardcoded triangle; Shutdown() deletes all three GL objects rather than leaking them across reloads. - unsafe confined to Configure() (VertexAttribPointer takes a raw offset pointer) via a narrow, documented override of Directory.Build.props' default — native graphics interop, not the kernel data structures docs/kernel-contract.md §7 was written against. engine.input: publishes IEngineInput (keyboard state) via Silk.NET.Input. Reuses Silk.NET's own Key enum rather than inventing one, same call as IEngineWindow.Native. Loads and constructs cleanly against a real window; nothing reacts to it yet since there's no gameplay code to. IScreenCapture (new, engine.render): reads the frame back via ReadPixels and writes a PNG. No SixLabors.ImageSharp — checked its license first and it isn't MIT/Apache (revenue-gated), which would have been a real surprise for downstream users of an MIT engine. PngWriter is a from-scratch encoder instead: ZLibStream (BCL, .NET 6+) for the one genuinely hard part, a correctly zlib-wrapped DEFLATE stream; chunk framing and CRC32 are small enough to get right and to verify by actually decoding files this wrote (done repeatedly, by hand, across this session). Engine.Host: "screenshot <path>" joins "r <plugin-id>" as a live stdin command, plus a non-interactive --screenshot/--screenshot-after- frames pair that captures once and exits — for scripts and agents that can't easily hold a pipe open into a long-running process. Real bug found and fixed in PluginHost, not specific to any one environment: loading a Contracts assembly into the Default ALC never set up resolution for ITS OWN dependencies. engine.windowing's and engine.render's Contracts both need Silk.NET packages and loaded fine anyway, by accident — Engine.Host references those two Contracts projects directly (to drive the windowed loop and screenshot capture), so their dependencies were already sitting in Engine.Host's own output directory. engine.input's Contracts has no such lucky coincidence and failed with a real FileNotFoundException. Fixed by hooking AssemblyLoadContext.Default.Resolving with an AssemblyDependencyResolver per loaded Contracts path — mirroring what PluginLoadContext already does for collectible ALCs, applied to the one path that never had it. Hooked once per process via a static list/flag, not per PluginHost instance, specifically to avoid a PluginHost instance becoming unreclaimable through its own event subscription. Not covered by automated tests, deliberately, same reasoning as the windowing/render pass: opening a real window and reading back a real framebuffer both need a real display. Verified by hand instead, documented above and in commit history rather than asserted. README status: M1 done, not "in progress." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N |