Files
aego/editor/shell/capture.go
sanspie ab6ea7ef58
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
github-native / linux amd64 native (push) Skipped
ci / checks (push) Successful in 6m33s
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 12m26s
ci / release (push) Skipped
github-native / darwin amd64 native (push) Canceled after 0s
github-native / windows amd64 native (push) Canceled after 0s
github-native / darwin arm64 native (push) Canceled after 0s
editor: improve scene tools and prepare native CI
2026-11-06 16:49:11 +03:00

99 lines
2.9 KiB
Go

package shell
import (
"aego/editor/core/services"
"errors"
"time"
"aego/editor/core/capture"
"aego/editor/core/command"
"aego/editor/core/context"
"aego/editor/panels/sceneview"
"aego/engine/i18n"
"aego/gpu"
"aego/platform/input"
)
const captureFlip = true
type sceneCapture struct {
tex gpu.TextureHandle
w, h int
name string
}
// Capture is requested during rendering; readback is only valid after EndFrame.
func (a *App) captureTexture(tex gpu.TextureHandle, w, h int, name string) {
a.captures = append(a.captures, sceneCapture{tex: tex, w: w, h: h, name: name})
}
// FrameFinished reads the target before the next UI update can resize it.
func (a *App) FrameFinished() {
for _, c := range a.captures {
a.saveCapture(c)
}
clear(a.captures)
a.captures = a.captures[:0]
}
func (a *App) saveCapture(c sceneCapture) {
dev := a.ctx.Engine.GPU()
if dev == nil {
return
}
buf := make([]byte, c.w*c.h*4)
if err := dev.ReadPixels(c.tex, 0, 0, c.w, c.h, buf); err != nil {
a.ctx.Notify(context.LevelError, i18n.Note("editor.shell.cannot_read_the_scene_view", "cannot read the Scene View: {error}", i18n.Str("error", err.Error())))
return
}
rel, err := capture.Save(a.opt.Project.Root, c.name, c.w, c.h, buf, captureFlip, time.Now())
if err != nil {
a.ctx.Notify(context.LevelError, err.Error())
return
}
a.ctx.Notify(context.LevelInfo, i18n.Note("editor.shell.saved_rel", "Saved {rel}", i18n.Str("rel", rel)))
}
func (a *App) registerCapture() error {
return a.ctx.Commands.Register(command.Desc{
ID: sceneview.CommandCapture, Name: "Capture Scene View", Category: "Scene", Order: 340,
Shortcut: command.Key(input.KeyF12, command.ModPrimary|command.ModShift),
Enabled: func() bool { return a.ctx.Doc() != nil },
Run: a.sceneView.RequestCapture,
})
}
func (a *App) AgentExporter() services.Exporter { return a.opt.Exporter }
func (a *App) CaptureAgentView(view string) ([]byte, uint64, string, error) {
d := a.ctx.Doc()
if d == nil {
return nil, 0, "", errors.New("no open scene")
}
t, tick := a.sceneView.LastRender()
if view == "game" {
t, tick = a.gameView.LastRender()
if playDoc := a.play.PlayDoc(); playDoc != nil {
d = playDoc
}
} else if view != "scene" {
return nil, 0, "", errors.New("unknown view")
}
if t == nil || d == nil {
return nil, 0, "", errors.New("view has no frame for this scene; show the view and start Play for a game capture, or use scene_run with shot")
}
if t.W <= 0 || t.H <= 0 || int64(t.W)*int64(t.H) > 16<<20 {
return nil, 0, "", errors.New("invalid capture size")
}
dev := a.ctx.Engine.GPU()
if dev == nil {
return nil, 0, "", errors.New("view has no graphics device")
}
buf := make([]byte, t.W*t.H*4)
if err := dev.ReadPixels(t.Texture, 0, 0, t.W, t.H, buf); err != nil {
return nil, 0, "", err
}
png, err := capture.Encode(t.W, t.H, buf, captureFlip)
return png, tick, d.Path, err
}