feat: AI screenshot capture for visual analysis
- 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.
This commit is contained in:
@@ -16,13 +16,15 @@ public sealed class AiCommandProcessor
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly Func<string, Mesh> _modelLoader;
|
||||
private readonly Action<string> _requestScreenshot;
|
||||
|
||||
public JsonSerializerOptions JsonOptions { get; }
|
||||
|
||||
public AiCommandProcessor(World world, Func<string, Mesh> modelLoader)
|
||||
public AiCommandProcessor(World world, Func<string, Mesh> modelLoader, Action<string> requestScreenshot)
|
||||
{
|
||||
_world = world;
|
||||
_modelLoader = modelLoader;
|
||||
_requestScreenshot = requestScreenshot;
|
||||
JsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
@@ -52,6 +54,7 @@ public sealed class AiCommandProcessor
|
||||
SetTransformCommand c => SetTransform(c),
|
||||
DeleteEntityCommand c => DeleteEntity(c),
|
||||
ListEntitiesCommand => ListEntities(),
|
||||
CaptureScreenshotCommand c => CaptureScreenshot(c),
|
||||
_ => AiCommandResult.Error($"Unknown command type: {command.Type}")
|
||||
};
|
||||
}
|
||||
@@ -122,4 +125,11 @@ public sealed class AiCommandProcessor
|
||||
var json = JsonSerializer.Serialize(names, JsonOptions);
|
||||
return AiCommandResult.Ok(json);
|
||||
}
|
||||
|
||||
private AiCommandResult CaptureScreenshot(CaptureScreenshotCommand command)
|
||||
{
|
||||
var path = command.OutputPath ?? $"screenshot_{DateTime.UtcNow:yyyyMMdd_HHmmss_fff}.png";
|
||||
_requestScreenshot(path);
|
||||
return AiCommandResult.Ok($"Screenshot requested: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Engine.AI.Commands;
|
||||
[JsonDerivedType(typeof(SetTransformCommand), "set_transform")]
|
||||
[JsonDerivedType(typeof(DeleteEntityCommand), "delete_entity")]
|
||||
[JsonDerivedType(typeof(ListEntitiesCommand), "list_entities")]
|
||||
[JsonDerivedType(typeof(CaptureScreenshotCommand), "capture_screenshot")]
|
||||
public abstract record AiCommand
|
||||
{
|
||||
public string Type => GetType().Name.Replace("Command", "").ToLowerInvariant();
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Engine.AI.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Request a screenshot of the current rendered frame for AI visual analysis.
|
||||
/// </summary>
|
||||
public sealed record CaptureScreenshotCommand : AiCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Output file path. If omitted, the engine chooses a default path.
|
||||
/// </summary>
|
||||
public string? OutputPath { get; init; }
|
||||
}
|
||||
@@ -70,6 +70,13 @@ public sealed class EngineMcpTools
|
||||
return EnqueueAndReturnMessage(cmd);
|
||||
}
|
||||
|
||||
[McpServerTool, Description("Capture a screenshot of the current rendered frame and save it to disk.")]
|
||||
public Task<string> CaptureScreenshot([Description("Optional output file path (default: screenshot_<timestamp>.png)")] string? outputPath = null)
|
||||
{
|
||||
var cmd = new CaptureScreenshotCommand { OutputPath = outputPath };
|
||||
return EnqueueAndReturnMessage(cmd);
|
||||
}
|
||||
|
||||
private async Task<string> EnqueueAndReturnMessage(AiCommand command)
|
||||
{
|
||||
var result = await _queue.EnqueueAsync(command).ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user