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
99 lines
2.9 KiB
Go
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
|
|
}
|