- renderTimer accumulates deltaTime each tick
- Render only when renderTimer >= 16.67ms
- If timer overshoots (slow frame), clamp to 0 to prevent spiral of death
- No Thread.Sleep/continue — loop runs continuously, just skips render
when not enough time has passed
- Physics + input + light animation: run at full speed every iteration
- Render: accumulator-based 60 FPS limiter — only renders when enough
time has accumulated (16.67ms). If not, Thread.Sleep(1) and continue.
- Video: captured at exactly 60 FPS, FFmpeg downsamples to 30
- Physics stays smooth even if render can't keep up
Swapchain format is B8G8R8A8_SRGB — vkCmdCopyImageToBuffer gives BGRA
data. Was converting BGRA→RGBA in C# and telling FFmpeg 'rgba' —
but SRGB data needs to stay as-is. Now:
- No BGRA→RGBA swap in ReadCapturedBuffer
- FFmpeg pixel_format=bgra (matches swapchain)
- FFmpeg handles SRGB→yuv420p conversion correctly
- Removed skipFrames logic — capture every frame for reliability
- FFmpeg framerate set to actual FPS (capped at 60) instead of hardcoded 30
- Added -vf fps=30 filter to downsample to 30fps in output
- First frame: CapturedFrame is null (no previous capture), skipped naturally
- Second frame onwards: data available from previous frame's GPU copy
- All 3 lights: intensity 8.0, range 15.5 (was 12/8/8, 60/40/40)
- Copy Parameters: also logs to console for debugging
- Clipboard text via ImGui.SetClipboardText (standard API)
- pyramid.obj: verified CCW via cross-product for all 6 faces
- diamond.obj: verified CCW for all 8 octahedron faces
- torus.obj: verified CCW — normal points outward from torus center
- cone.obj: verified CCW for side faces (radial outward) and bottom (-Y)
- Removed Grid entity — balls were falling through it (no physics)
- Removed 'Grid' from castShadow exclusion list
- All objects now render correctly with CULL_MODE_BACK
- Changed from Thread to Task.Run for MCP server
- Start delay: 10 frames (was 3) — let scene fully initialize first
- Kestrel on ThreadPool instead of dedicated thread
- ImGui.GetIO().MousePos set from IInputState.MouseX/MouseY each frame
- ImGui.GetIO().MouseDown[0-2] set from MouseLeft/Right/Middle
- DisplaySize also updated each frame (was only set at init)
- Done before BeginImGuiFrame() / ImGui.NewFrame()
- Light position animated with sin/cos combinations at different frequencies
- XZ plane: figure-8 pattern (sin*0.7 + cos*0.3, cos*0.6 + sin*0.4)
- Y: oscillates 7-17 with sin*0.5
- Shadows move dynamically as light orbits the scene
- Light moved to (0, 20, 0) — static, no physics body
- Intensity 50, range 60, warm white color
- Shadow map renders from this fixed position
- LightBall removed, replaced with MainLight entity
- Only one light source: the physics LightBall
- Intensity 30 (was 20), range 40 (was 30) for stronger illumination
- Static MainLight removed so renderer picks the dynamic ball
- LightBall entity: sphere mesh + DynamicSphere physics + Light.Point
- Renderer reads light position from Transform (not static Light.Position)
so the light moves with the physics body
- Light intensity 20, range 30, warm color (1.0, 0.9, 0.7)
- Ball falls from y=15, bounces on floor, light follows it
- Renderer: prefers Light+Transform pair for dynamic position, falls back to Light only
- UBO expanded from 64 to 128 bytes: vp(64) + lightPos(16) + lightColor(16)
- Vertex shader: passes point light UBO data through
- Fragment shader: refactored calcLight() function, shared by directional + point
- Point light: Unity-style attenuation pow(1 - dist/range, 2)
- Directional light reduced intensity (0.4) to balance with point light
- Point light: position (0,8,0), warm color (1,0.9,0.7), intensity 15, range 25
- Renderer: reads Light component from ECS, packs into UBO with Marshal.StructureToPtr
- Scene: MainLight entity with Light.Point at (0,8,0)
- 0 C# struct changes — pure UBO layout + shader upgrade
- MCP server (Kestrel) launched on background thread after 3 frames
to avoid conflicting with Vulkan/SDL3 initialization
- --mcp-port 0 (default) = disabled, scene works as before
- --mcp-port 5000 = MCP starts after 3rd frame
- Reduced extra balls from 30 to 10 for stability
- Engine.AI/McpEngineServerHost reverted to original (no WebApplicationOptions)
- Engine.AI referenced in CortexEngine.App.csproj
- AiCommandProcessor created with world + mesh loader + screenshot callback
- AiCommandQueue marshals commands from MCP thread to main thread
- MCP HTTP server (Kestrel) started on separate background thread
- --mcp-port N flag (default 0 = disabled) controls MCP server
- queue.ProcessPending() called before render each frame
- queue.CompletePendingScreenshots() called after render
- ImGui overlay shows MCP status (port or disabled)
- DummyScreenshotProvider placeholder (real capture = future checkpoint)
- 7 MCP tools: spawn_model, set_transform, set_material, delete_entity,
list_entities, get_world_state, capture_screenshot
- MCP server has 2s startup delay to avoid blocking Vulkan init
- Default: MCP disabled (--mcp-port 0), scene + physics + ImGui work
- Engine.Physics (JoltPhysicsSharp) integrated into CortexEngine.App
- 4 cubes: DynamicBox, mass proportional to scale, fall from height 5-10
- 3 spheres: DynamicSphere, mass 1.5, fall from height 7-12
- Floor: StaticBox (20x0.5x20), friction 0.8
- Torus knot + grid: no physics (visual only)
- Physics bodies initialized lazily on first frame (IsInitialized flag)
- e.Set() moved outside world.Each to avoid mutation during iteration
- physicsWorld.Update + SyncTransforms every frame
- Removed manual rotation — physics handles movement now
- Camera at (0, 5, -15) for better view of falling objects
- 0 validation errors, ~1300 FPS
- 10 entities in ECS World with Transform + Mesh components
- Torus knot (center, scale 1.5) rotating around Y
- 4 cubes (left/right/front/back) rotating at different speeds
- 3 spheres (procedural, 32x16) rotating around X
- Floor (20x20 flat cube) + grid (ProceduralMesh.CreateGrid)
- Camera at (0, 3, -12) looking at (0, 0.5, 0)
- Mesh cache: buffers created lazily per entity ID on first frame
- Diffuse lighting on all objects
- 0 validation errors, ~2300 FPS
- VulkanRenderer: mesh cache by entity ID, lazy buffer creation
- RenderWorld iterates entities with Transform+Mesh, draws each with
per-entity model matrix via push constants
- Program.cs: creates scene with 1 torus knot (center, scale 1.5) +
6 cubes at different positions, all rotating around Y at different speeds
- Camera at (0, 2, -8) looking at origin, FreeFly controls
- 0 validation errors, ~2100 FPS with 7 objects
- Program.cs: create Camera entity in ECS World, FreeFlyCameraController
with WASD movement, Q/E up/down, Shift boost, right-click mouse look
- VulkanRenderer.RenderWorld: queries Camera from World, computes VP
from Camera.GetViewMatrix() * GetProjectionMatrix()
- Camera aspect ratio auto-updated on window resize
- Fallback to hardcoded VP if no Camera entity found
- Torus knot still rotating via push constant model matrix
- 0 validation errors
ImGui:
- ImGui.NET 1.91.6.1 NuGet package
- VulkanImGui: font texture upload, ImGui pipeline (blending, no depth), vertex/index buffer streaming
- GLSL 450 ImGui shaders (vert: scale/translate push constants, frag: font texture sampler)
- Compiled to SPIR-V via @webgpu/glslang
- Debug overlay: FPS, camera position, entity count, light list
- 1600+ FPS with ImGui active
Screenshot:
- vkCmdCopyImageToBuffer embedded in main command buffer
- Layout transition PresentSrcKHR→TransferSrcOptimal→PresentSrcKHR
- Deferred read: staging buffer read on next frame after fence
- BMP format written directly to FileStream (row by row)
- All 16 camera tour screenshots captured (1280x720x32bpp)
All 66 tests pass.
OpenTK renderer has matrix/swap issues causing garbled output.
Raylib restored as default. OpenTK project preserved for debugging.
Normal PBR shader restored in OpenTKRenderer.
- ObjectLayerPairFilterTable blocks all collisions by default in C# binding;
explicitly EnableCollision for all layer pairs (NonMoving/Moving)
- BoxShape takes half-extents, not full size: DynamicBox(scale * 0.5f)
- Floor visual thickness reduced (scale Y 1→0.5), top at y=0 matching collider
- Floor lowered to y=-0.5
- Physics works: cubes and spheres fall and land on floor, no clipping
BroadPhaseLayerInterfaceTable, ObjectLayerPairFilterTable, and
ObjectVsBroadPhaseLayerFilterTable were local variables in the
constructor. After GC collected them, Jolt crashed with dangling
native pointers during PhysicsSystem.Update(). Now stored as readonly
fields with proper Dispose in cleanup. Also fixed Flecs table lock
assertion by collecting RigidBody init list before calling entity.Set().
- Add ICameraController interface and FreeFlyCameraController.
- WASD moves, Q/E up/down, Shift sprint, right-mouse + mouse look.
- Toggle between Orbit and FreeFly with F key.
- OrbitCameraController now implements ICameraController.
- Update Program.cs to switch active controller on F.
- Update CORTEX_ENGINE_ARCHITECTURE.md with camera controls.
- Debug/Release/ReleaseAOT all build.
- Add get_world_state AI command that dumps ECS entities with Transform,
Camera, Material, Light, and Mesh summaries.
- Add set_material AI command to update albedo/roughness/metallic/texture.
- Expose both commands as MCP HTTP tools and stdio tools.
- Add Light component and support up to 4 directional lights via a Vulkan
uniform buffer (descriptor set 0) with std140 layout.
- Move per-frame lighting/camera data into the uniform buffer; push constants
now carry only MVP + material properties (96 bytes).
- Add Texture class for PNG loading and Vulkan image/view/sampler creation.
- Add per-entity combined image sampler descriptor set (set 1) and use it
for albedo texture sampling in the fragment shader.
- Generate a checkerboard floor texture in Program.cs.
- Add McpStdioServer for headless stdio MCP (Claude Desktop compatible).
- Add claude_desktop_config.json and scripts/start_mcp_engine.sh.
- Update CORTEX_ENGINE_ARCHITECTURE.md with runtime notes, CLI arguments,
Vulkan pipeline details, and MCP client configuration.
- All configs (Debug/Release/ReleaseAOT) build successfully.
- Add Material component with Albedo, Roughness, Metallic.
- MeshRenderer reads Material and tints vertex color; falls back to default.
- Add Blinn-Phong specular using camera position pushed via push constants.
- Add floor plane and grid mesh in Program.cs.
- Add OrbitCameraController with right-mouse orbit and mouse-wheel zoom.
- Forward SDL events to InputMapping from Sdl3Window.PumpEvents.
- Update main loop to update camera and aspect ratio on resize.
- Recompile SPIR-V shaders.
- Update CORTEX_ENGINE_ARCHITECTURE.md with Material, lighting, and orbit camera.
- Add ScreenshotCapture class to Engine.Graphics using Vulkan image readback.
- Integrate screenshot capture into MeshRenderer; request triggers readback on next frame.
- Add SixLabors.ImageSharp 3.1.11 for PNG encoding (patched for CVE-2025-54575).
- Add capture_screenshot AI command and MCP tool.
- Wire screenshot request into Program.cs with demo command.
- Expose swapchain surface format and image accessor for readback.
- Add Screenshots/ to .gitignore.
- Update CORTEX_ENGINE_ARCHITECTURE.md with MCP, Silk.NET.Vulkan, ImageSharp, and screenshot capture.
- Add ModelContextProtocol + ModelContextProtocol.AspNetCore 1.4.0 to Engine.AI.
- Expose AI commands as MCP tools: spawn_model, set_transform, delete_entity, list_entities.
- Add AiCommandQueue to marshal commands from the MCP server thread to the main thread.
- Start in-process HTTP MCP server in Program.cs (Debug/Release only; excluded in ReleaseAOT).
- Add --mcp-port CLI argument to configure the MCP server port.
- Fix Flecs.NET package conditions to include ReleaseAOT config.