Commit Graph
81 Commits
Author SHA1 Message Date
emil28092005 905dd839c8 fix: remove static MainLight, increase LightBall intensity to 30, range 40
- 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
2026-06-18 19:28:17 +03:00
emil28092005 6a7d6ee3eb feat: single point light only — dark scene, dynamic light ball visible
- Removed directional light from fragment shader
- Ambient reduced to near-zero (0.01) — scene is dark
- Only light source is the physics LightBall falling from y=15
- Objects near the ball are illuminated, far objects are in darkness
- Dramatic contrast shows dynamic lighting clearly
2026-06-18 19:27:05 +03:00
emil28092005 e6cc9e6c4e feat: dynamic point light on a physics ball — falls and illuminates
- 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
2026-06-18 19:25:02 +03:00
emil28092005 f718e6de49 feat: PBR point light — Unity-style attenuation, warm glow
- 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
2026-06-18 19:23:00 +03:00
emil28092005 22748ed9ba feat: PBR shading — Cook-Torrance BRDF with ACES tonemapping
- Vertex shader: passes world position, world normal, albedo to fragment
- Fragment shader: full PBR implementation
  - D term: Trowbridge-Reitz GGX distribution
  - G term: Smith geometry with Schlick-GGX
  - F term: Schlick Fresnel approximation
  - kD/kS split based on metallic
  - Directional light (0.5, 0.8, 0.3) with warm color
  - Ambient term (0.15, 0.18, 0.22) for fill light
  - ACES filmic tonemapping
  - Gamma 2.2 correction
- Fixed roughness=0.5, metallic=0.1 (per-object materials = future)
- No C# code changes — pure shader upgrade
2026-06-18 19:18:01 +03:00
emil28092005 50e77e705b fix: MCP server starts after 3rd frame to avoid blocking Vulkan init
- 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)
2026-06-18 19:12:11 +03:00
emil28092005 1f7538383e feat: integrate AI/MCP — 7 tools available via --mcp-port flag
- 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
2026-06-18 18:56:53 +03:00
emil28092005 bfde04b381 feat: ImGui debug overlay — FPS, camera position, entity count
- ImGui.NET 1.91.6.1 integrated into Engine.Graphics.Vulkan
- VulkanImGui.cs: font atlas upload (staging → VkImage), sampler,
  descriptor set (COMBINED_IMAGE_SAMPLER), separate pipeline with
  alpha blending, no depth write, dynamic scissor
- ImGui shaders: imgui.vert (ortho MVP push constant) + imgui.frag
  (font texture sampler)
- Dynamic vertex/index buffers (HOST_VISIBLE, grow on demand)
- IRenderer interface: BeginImGuiFrame/EndImGuiFrame default methods
- Program.cs: debug window with FPS, camera pos/target, entity count
- 0 critical validation errors, ~750 FPS with physics + ImGui
2026-06-18 17:34:42 +03:00
emil28092005 e67e312d28 feat: Jolt physics — cubes and spheres fall with gravity, hit floor
- 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
2026-06-18 17:18:59 +03:00
emil28092005 609b11e3a3 feat: full engine scene — torus knot + 4 cubes + 3 spheres + floor + grid
- 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
2026-06-18 17:10:35 +03:00
emil28092005 e36cbc7774 feat: diffuse lighting — normals passed to fragment shader
- Vertex shader: pass normal through mat3(model) to fragment
- Fragment shader: directional light (0.5, 0.8, 0.3) with 0.25 ambient
  + 0.75 diffuse = faces now have depth and 3D form
- Torus knot has smooth normals (vn in OBJ), cubes have face normals
- 0 validation errors, ~2300 FPS
2026-06-18 17:03:15 +03:00
emil28092005 a4914f28c5 feat: ECS scene with 7 objects — torus knot + 6 rotating cubes
- 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
2026-06-18 17:00:14 +03:00
emil28092005 398cc88ae2 fix: remove row_major from GLSL — fixes fish-eye and broken projection
Root cause: System.Numerics.Matrix4x4 uses row-major storage with row-vector
convention (v * M). GLSL with layout(row_major) does column-vector (M * v),
which effectively transposes the matrix, breaking the projection.

Fix: remove row_major from layout qualifiers. GLSL then interprets the
row-major data as column-major (effectively transposing it), so M * v in
GLSL equals v * M in System.Numerics — correct result.

The Y-flip (M22 *= -1) is still needed for Vulkan's Y-down clip space.
2026-06-18 16:56:44 +03:00
emil28092005 44bae31495 fix: flip projection Y for Vulkan (M22 *= -1) — fixes fish-eye distortion
System.Numerics.CreatePerspectiveFieldOfView uses DirectX convention (Y up).
Vulkan clip space has Y down. Negating M22 corrects the projection.
Depth is already [0,1] in System.Numerics (DirectX-style), no remap needed.
2026-06-18 16:53:48 +03:00
emil28092005 fcbc42959e feat: FreeFlyCameraController — interactive WASD + mouse look
- 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
2026-06-18 16:49:54 +03:00
emil28092005 2886dd77f2 feat: render torus knot (4800 verts, 9600 faces, smooth normals)
- Load Content/torusknot.obj instead of cube.obj
- 28800 vertices (duplicated per-face), 28800 indices
- OBJ has vn lines so smooth normals are used (not face normals)
- Removed slide animation, kept Y-axis rotation
- Camera z=-6, FOV 45°
- 0 validation errors, ~3000 FPS
2026-06-18 16:46:27 +03:00
emil28092005 0978b82322 fix: remove projection matrix hacks — back to clean FOV 45° + z=-5
- Removed M22 Y-flip and M33/M43 depth remap that caused edge distortion
- System.Numerics.CreatePerspectiveFieldOfView works as-is with row_major
- FOV 45°, camera z=-5, 1 cube sliding left-right + rotating
2026-06-18 13:29:26 +03:00
emil28092005 00f23187d5 feat: 1 cube slowly sliding left-right (sin wave) + rotating 2026-06-18 13:27:36 +03:00
emil28092005 a801e7fb16 fix: Vulkan projection matrix — Y-flip + depth [0,1] remap
- Negate proj.M22 for Vulkan Y-down coordinate system
- Remap depth from OpenGL [-1,1] to Vulkan [0,1]: M33*0.5+0.5, M43*0.5+0.5
- FOV 45°, camera z=-5, cubes at x=±2
- 0 validation errors
2026-06-18 13:25:59 +03:00
emil28092005 ce3dc8c41b fix: FOV 72°, camera z=-10, cubes 6 units apart 2026-06-18 13:24:26 +03:00
emil28092005 1a277abc7a fix: wider FOV (81°) for more distance 2026-06-18 13:23:19 +03:00
emil28092005 c9358bf080 fix: wider FOV (60°) + camera z=-6 for better view 2026-06-18 13:22:41 +03:00
emil28092005 51f94b8923 fix: move camera to z=-12 2026-06-18 13:21:39 +03:00
emil28092005 45fcccbd34 fix: move camera to z=-7 for more distance 2026-06-18 13:20:50 +03:00
emil28092005 edfcf33578 feat: render 2 rotating cubes side by side
- Push constant changed from float angle (4B) to mat4 model (64B)
- Two draw calls with different model matrices (x=-1.5 and x=+1.5)
- Both cubes rotate around Y axis at 0.2 rad/s
- 0 validation errors
2026-06-18 13:20:22 +03:00
emil28092005 567d88825d fix: slow down rotation to 0.2 rad/s 2026-06-18 13:18:45 +03:00
emil28092005 0541f5d13c feat: rotate cube around Y axis instead of Z 2026-06-18 13:18:07 +03:00
emil28092005 f8cbacb422 feat: slow cube rotation (0.5 rad/s) 2026-06-18 13:16:46 +03:00
emil28092005 441ce1f511 fix: move camera to z=-4 so cube is further away 2026-06-18 13:15:17 +03:00
emil28092005 bdd4d3eaf8 feat: cube from OBJ with depth buffer, no rotation
- Load Content/cube.obj via ObjLoader (36 vertices, 36 indices)
- Depth buffer re-enabled: depth test + write, CompareOp=Less
- Depth image transition + depth attachment in rendering info
- Push constant angle=0 (no rotation)
- Camera at z=-2, perspective FOV 45°
- UINT32 index type (fixed in previous commit)
- Face normals computed by ObjLoader (no vn in cube.obj)
- 0 validation errors
2026-06-18 13:14:13 +03:00
emil28092005 233041ab00 fix: VK_INDEX_TYPE_UINT32 (was UINT16) + remove unused depth from pipeline
- vkCmdBindIndexBuffer indexType changed from 0 (UINT16) to 1 (UINT32)
  to match uint[] index data — this was causing black screen
- Removed depth stencil state, depth attachment format, and depth
  rendering attachment from pipeline/renderer (depth buffer still
  created in swapchain but unused)
2026-06-18 12:59:01 +03:00
emil28092005 aca8d1ab4d fix: correct matrix order (proj*view) + Z depth range for Vulkan
- VP matrix order: proj*view (GLSL column-vector convention)
- Z correction in vertex shader: gl_Position.z = (z+w)*0.5 (OpenGL [-1,1] → Vulkan [0,1])
- Depth test temporarily disabled for debugging
2026-06-18 12:47:20 +03:00
emil28092005 d620bd537f revert: back to triangle with 3D rotation + depth buffer
- Hardcoded triangle (RGB vertices) with index buffer
- 3D rotation: RotateZ * RotateX
- Depth buffer + UBO + push constant model matrix retained
- Camera at z=-3, perspective FOV 45°
2026-06-18 12:44:06 +03:00
emil28092005 e740cf1214 fix: compute face normals when OBJ has no vn lines
- ObjLoader.ParseFace: if no normals in OBJ, compute face normal from
  first triangle using MeshMath.ComputeFaceNormal and apply to all face vertices
- cube.obj has no vn lines, so all faces previously had normal (0,1,0)
- Now each face has correct outward-facing normal for proper lighting
2026-06-18 12:42:13 +03:00
emil28092005 d6ca80c678 fix: add normal-based diffuse lighting so cube is visible as 3D
- Fragment shader: directional light dot(normal, lightDir) with 0.2 ambient
- Vertex shader: pass normal through mat3(model) to fragment
- Same-color faces now have different shading based on orientation
2026-06-18 12:39:51 +03:00
emil28092005 d6bd1807cf feat: depth buffer + 3D model matrix — rotating cube now looks correct
- Depth image (D32_SFLOAT) + image view created in VulkanSwapchain
- VkPipelineDepthStencilStateCreateInfo: depth test + write enabled, CompareOp=Less
- Push constant changed from float angle (4B) to mat4 model (64B)
- 3D rotation: RotateY * RotateX in renderer, applied via push constant
- Vertex shader: gl_Position = vp * model * vec4(pos, 1.0)
- Depth attachment in VkRenderingInfo + depth clear (1.0)
- Depth image layout transition (Undefined → DepthStencilAttachmentOptimal)
- New Vulkan functions: vkCreateImage, vkDestroyImage, vkGetImageMemoryRequirements, vkBindImageMemory
- New structs: VkPipelineDepthStencilStateCreateInfo, VkStencilOpState, VkImageCreateInfo
- New enums: VkCompareOp, VkImageType, VkImageTiling
- 0 validation errors
2026-06-18 02:14:54 +03:00
emil28092005 fde8b0f63a feat: index buffer + OBJ cube rendering
- VulkanIndexBuffer.cs: staging → device-local index buffer (uint16 indices)
- vkCmdBindIndexBuffer + vkCmdDrawIndexed added to Vk.cs
- VulkanRenderer loads Content/cube.obj via ObjLoader (36 vertices, 12 faces)
- Replaced hardcoded triangle with rotating cube
- 0 validation errors, ~3800 FPS
2026-06-18 02:05:15 +03:00
emil28092005 4f3810010a feat: push constants + UBO descriptor sets — rotating triangle with VP matrix
- Push constants: rotation angle (float) sent to vertex shader via vkCmdPushConstants
- UBO + descriptor sets: VP matrix (mat4) in uniform buffer, bound via VkDescriptorSet
- New Vulkan functions: vkCreateDescriptorSetLayout, vkCreateDescriptorPool,
  vkAllocateDescriptorSets, vkUpdateDescriptorSets, vkCmdBindDescriptorSets,
  vkCmdPushConstants
- New structs: VkPushConstantRange, VkDescriptorSetLayoutBinding,
  VkDescriptorSetLayoutCreateInfo, VkDescriptorPoolCreateInfo,
  VkDescriptorPoolSize, VkDescriptorSetAllocateInfo, VkWriteDescriptorSet,
  VkDescriptorBufferInfo
- Per-frame UBO buffers (HOST_VISIBLE|HOST_COHERENT, 64 bytes each)
- Perspective projection + look-at camera (static, no ECS yet)
- Cleaned up debug logging in VulkanContext
- Zero validation errors
2026-06-18 01:59:41 +03:00
emil28092005 2e0970e769 feat: rebuild Vulkan renderer from scratch — pure P/Invoke triangle (Vulkan 1.3)
- Complete rewrite of Engine.Graphics.Vulkan with pure P/Invoke (no wrapper libs)
- Vulkan 1.3: dynamic rendering (vkCmdBeginRendering/vkCmdEndRendering),
  synchronization2 (vkQueueSubmit2, vkCmdPipelineBarrier2)
- Split types into VulkanHandles.cs, VulkanEnums.cs, VulkanStructs.cs
- Staging buffer → device-local vertex buffer pattern
- Correct swapchain semaphore indexing (per-image, not per-frame-in-flight)
- VK_EXT_debug_utils debug messenger with validation layer fallback
- Dynamic viewport/scissor (no pipeline recreation on resize)
- Simplified Program.cs to triangle-only rendering
- Removed old Silk.NET renderer, ImGui, PBR shaders, screenshot code
- Updated VULKAN_IMPLEMENTATION_PLAN.md with full architecture decisions
2026-06-18 01:49:25 +03:00
emil28092005 ee98e4ad08 fix(window): ensure SDL3/Wayland window is mapped by pumping events after ShowWindow
- Remove ALWAYS_ON_TOP and debug print hacks.
- Pump SDL events after SDL_ShowWindow so the Wayland compositor maps the window.
- Clean run.sh: do not force DISPLAY or SDL_VIDEODRIVER, let SDL3 auto-detect.
2026-06-18 00:38:16 +03:00
emil28092005 aa93b55a74 fix: window now visible — SDL_ShowWindow + Wayland support in run.sh
- Added SDL_ShowWindow + SDL_RaiseWindow after window creation
- run.sh: auto-detect Wayland vs X11, set SDL_VIDEODRIVER accordingly
- Wayland session uses SDL_VIDEODRIVER=wayland (bundled SDL3 lacks X11 support)
- Engine runs at 1600+ FPS, window visible on Wayland desktop
- Screenshots confirm 3D geometry rendering correctly
2026-06-18 00:22:27 +03:00
emil28092005 46a04850e3 fix: rendering now works — three critical bugs fixed
1. VkColorComponentFlags: wrong bit values (0x10-0x80 instead of 0x01-0x08)
   → colorWriteMask was 0xF0 instead of 0x0F → no color channels written

2. VkClearValue: was Sequential layout instead of Explicit (union)
   → depthStencil clear value written at wrong offset → depth buffer
   cleared to 0.0 instead of 1.0 → all fragments failed depth test

3. Vulkan clip space correction in vertex shader:
   gl_Position.y = -gl_Position.y (Vulkan Y-down vs OpenGL Y-up)
   gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5 (Z [0,1] vs [-1,1])

Also:
- PushConstantSize fixed from 128 to 144 (two mat4 + vec4)
- Dynamic viewport/scissor state added to pipeline
- Push constants use column_major (default) to match System.Numerics row-major layout
- Camera.GetProjectionMatrix reverted to pure OpenGL-style (correction in shader)
- Screenshots show actual 3D geometry (calibration cubes visible)
- 66/66 tests pass
2026-06-18 00:14:40 +03:00
emil28092005 749fbc8df4 feat: ImGui integration + real screenshot capture via Vulkan
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.
2026-06-17 23:39:38 +03:00
emil28092005 544f6b59c0 feat: real screenshot capture — BMP from swapchain image via vkCmdCopyImageToBuffer
- Added vkCmdCopyImageToBuffer to P/Invoke layer
- Screenshot embedded in main command buffer (barrier PresentSrcKHR→TransferSrc, copy, barrier back)
- Deferred read: staging buffer read on next frame after fence signaled
- BMP format written directly to FileStream (row by row, avoids large heap allocations)
- BGRA→BGRA direct copy for B8G8R8A8 swapchain format
- All 16 camera tour screenshots captured successfully (1280x720x32bpp)
- 66/66 tests pass
2026-06-17 23:27:58 +03:00
emil28092005 80238b08f5 feat: pure Vulkan P/Invoke renderer — no wrappers, SDL3 window, SPIR-V shaders
- Engine.Graphics: restored interfaces (IRenderContext, IRenderer, RenderBackendFactory),
  loaders (ObjLoader, GltfLoader), ProceduralMesh, MeshMath, SceneSerializer
- Engine.Graphics.Vulkan: pure P/Invoke to libvulkan.so.1/vulkan-1.dll
  - VulkanNative: library loading, function pointer loading via vkGetInstanceProcAddr
  - Vk: static function cache for ~80 Vulkan functions, all delegate types
  - VulkanTypes: ~50 structs, ~20 enums matching Vulkan C headers
  - VulkanContext: instance, physical device, logical device, surface, memory types
  - VulkanSwapchain: swapchain, image views, depth image, render pass, framebuffers
  - VulkanPipeline: graphics pipeline, descriptor set layout, shader modules
  - VulkanBuffer: vertex/index/uniform buffers, staging, memory allocation
  - VulkanRenderer: command buffers, sync (semaphores/fences), render loop, 2 frames in flight
  - GLSL 450 shaders: PBR lighting (Fresnel, ACES, gamma), directional+point lights
  - Compiled to SPIR-V via @webgpu/glslang WASM
- CortexEngine.App: switched to vulkan backend, removed Raylib/OpenTK/ImGui refs
- Tests: all 66 pass (ObjLoader, MeshMath, ProceduralMesh, SceneSerializer, etc.)
- Camera tour: 16 poses, screenshots captured, clean shutdown
2026-06-17 23:03:24 +03:00
emil28092005 dd105ea4af chore: remove all graphics backends (Raylib, OpenTK, Vulkan/Silk.NET) — prepare for pure Vulkan P/Invoke rewrite 2026-06-17 22:12:51 +03:00
emil28092005 ff4a3faae9 revert: switch back to Raylib default, keep OpenTK for future
OpenTK renderer has matrix/swap issues causing garbled output.
Raylib restored as default. OpenTK project preserved for debugging.
Normal PBR shader restored in OpenTKRenderer.
2026-06-17 21:35:20 +03:00
emil28092005 f0af0a6b5e fix: rewrite OpenTK renderer from scratch — correct matrix transpose
- SetUniformMat4: copies Matrix4 to float[16] row-major, passes with
  transpose=true (OpenGL transposes row-major → column-major)
- Previous version used transpose=false with manual column-major copy
  which caused double-transpose → garbage rendering (strobe)
- VertexAttribPointer with stride=0 (tightly packed per-attribute buffers)
- MakeCurrent() explicitly in constructor to ensure GL context is ready
- 66/66 tests, 1400+ FPS
2026-06-17 21:28:53 +03:00
emil28092005 270806a189 feat: OpenTK 4.x OpenGL backend — replaces Silk.NET, default renderer
- Deleted Engine.Graphics.OpenGL (Silk.NET) project
- Created Engine.Graphics.OpenTK with OpenTK 4.9.4
- OpenTKWindow: GameWindow + ProcessEvents() (non-blocking), SwapBuffers()
- OpenTKInputState: KeyboardState/MouseState → Engine.Core.Key
- OpenTKRenderer: raw OpenGL 3.3 Core, managed arrays (no unsafe),
  OpenTK.Mathematics.Matrix4 (column-major, no transpose needed),
  shadow FBO with depth texture, PCF 3x3, full PBR shader
- OpenTKRenderContext + OpenTKBackendRegistrar ('opentk')
- Program.cs: default backend is 'opentk'
- 66/66 tests, 0 errors, 1600 FPS (VSync off)
2026-06-17 21:24:33 +03:00
emil28092005 5687e97a09 revert: switch back to Raylib as default backend
OpenGL backend has rendering issues (stripes) that need visual debugging.
Raylib restored as default. OpenGL backend code preserved for future work.
2026-06-17 21:05:59 +03:00