diff --git a/contour_layer.go b/contour_layer.go index 0f45398..dfe8bc2 100644 --- a/contour_layer.go +++ b/contour_layer.go @@ -1,10 +1,11 @@ package main import ( + sg "github.com/d2fn/sumi/internal/graphics" "github.com/gen2brain/raylib-go/raylib" + "image/color" "math" "math/rand" - sg "github.com/d2fn/sumi/internal/graphics" ) type ContourLayer struct { @@ -13,12 +14,12 @@ type ContourLayer struct { maxActors uint32 actors []*Actor actorIndex uint32 - actorColor sg.Color + actorColor color.RGBA loActorAngle float32 hiActorAngle float32 } -func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color sg.Color, loActorAngle float32, hiActorAngle float32) *ContourLayer { +func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color color.RGBA, loActorAngle float32, hiActorAngle float32) *ContourLayer { maxActors := 200000 @@ -38,13 +39,13 @@ func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color sg.Color return &layer } -func (s *ContourLayer) AddActors(color sg.Color, n, sourceWidth, sourceHeight int32) { +func (s *ContourLayer) AddActors(color color.RGBA, n, sourceWidth, sourceHeight int32) { for range n { x := s.rng.Int31() % sourceWidth y := s.rng.Int31() % sourceHeight newActor := &Actor{ - position: sg.Point { X: float32(x), Y: float32(y) }, + position: sg.Point{X: float32(x), Y: float32(y)}, field: s.field, stepSize: 1, color: s.actorColor, @@ -56,16 +57,15 @@ func (s *ContourLayer) AddActors(color sg.Color, n, sourceWidth, sourceHeight in } } -func (s *ContourLayer) Update(ctx *Env) { - s.AddActors(s.actorColor, 100, int32(ctx.Graphics.Layout.Graphics.Width), int32(ctx.Graphics.Layout.Graphics.Height)) +func (s *ContourLayer) Update(env *Env, g *sg.Graphics) { + s.AddActors(s.actorColor, 100, g.WidthInt32(), g.HeightInt32()) } -func (s *ContourLayer) Draw(ctx *Env) { - g := ctx.Graphics +func (s *ContourLayer) Draw(env *Env, g *sg.Graphics) { g.BeginAdditiveBlend() for _, actor := range s.actors { if actor != nil { - actor.Draw(ctx) + actor.Draw(env, g) } } g.EndBlend() @@ -79,18 +79,17 @@ type Actor struct { position sg.Point field Field stepSize float32 - color sg.Color + color color.RGBA loAngle float32 hiAngle float32 } -func (a *Actor) Draw(ctx *Env) { +func (a *Actor) Draw(env *Env, g *sg.Graphics) { v := a.field.Get(a.position.X, a.position.Y) rad := rl.Remap(v, 0, 1, a.loAngle, a.hiAngle) - nextPosition := sg.Point {X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))} + nextPosition := sg.Point{X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))} //nextPosition := rl.Vector2{X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))} - g := ctx.Graphics g.SetStrokeWeight(1.0) g.SetStroke(true) g.SetStrokeColor(a.color) @@ -107,8 +106,7 @@ type Worm struct { renderPct float32 } -func (w *Worm) Draw(ctx *Env) { - g := ctx.Graphics +func (w *Worm) Draw(env *Env, g *sg.Graphics) { g.PushMatrix() g.Translate(w.position) //rl.PushMatrix() @@ -122,8 +120,8 @@ func (w *Worm) Draw(ctx *Env) { deltaAngle := angle - lastAngle if !nudged { rad := float64(deltaAngle * math.Pi / 180.0) - nudge := sg.Vec {X: float32(w.stepSize) * float32(math.Cos(rad)), Y: float32(w.stepSize) * float32(math.Sin(rad))} - w.position = w.position.Add(nudge)//rl.Vector2Add(w.position, nudge) + nudge := sg.Vec{X: float32(w.stepSize) * float32(math.Cos(rad)), Y: float32(w.stepSize) * float32(math.Sin(rad))} + w.position = w.position.Add(nudge) //rl.Vector2Add(w.position, nudge) nudged = true } rl.Rotatef(deltaAngle, 0, 0, 1) diff --git a/env.go b/env.go index 79c799f..c3bda62 100644 --- a/env.go +++ b/env.go @@ -2,14 +2,28 @@ package main import ( sg "github.com/d2fn/sumi/internal/graphics" + "time" ) /** Env **/ type Env struct { - Layout Layout - Time float64 - Ports map[string]float64 - Graphics sg.Graphics + Time time.Time + Frame uint + Ports map[string]float64 + Layout Layout + Viewport *sg.Graphics + Window *sg.Graphics + Controls *sg.Graphics + Offscreen *sg.Graphics + Storage *Storage +} + +func NewEnv() *Env { + return &Env{ + Time: time.Now(), + Frame: 0, + Ports: make(map[string]float64), + } } type Layout struct { @@ -22,16 +36,5 @@ type Layout struct { // bounds of the region of the app window reserved for rendering the graphics buffer Viewport sg.Rect // bounds of the off screen graphics buffer where rendering happens - Graphics sg.Rect + Offscreen sg.Rect } - - -func (ctx *Env) GetGraphicsWidth() float32 { - return ctx.Graphics.Bounds.Width -} - -func (ctx *Env) GetGraphicsHeight() float32 { - return ctx.Graphics.Bounds.Height -} - - diff --git a/field.go b/field.go index c6e6d46..559d877 100644 --- a/field.go +++ b/field.go @@ -2,6 +2,7 @@ package main import ( "fmt" + sg "github.com/d2fn/sumi/internal/graphics" rl "github.com/gen2brain/raylib-go/raylib" "github.com/ojrac/opensimplex-go" "math" @@ -105,11 +106,13 @@ func (fl *FieldLayer) Update(env *Env) { } -func (fl *FieldLayer) Draw(env *Env) { - rl.ClearBackground(rl.Blank) - rl.BeginBlendMode(rl.BlendAlphaPremultiply) - for x := range int32(env.GetGraphicsHeight()) { - for y := range int32(env.GetGraphicsHeight()) { +func (fl *FieldLayer) Draw(env *Env, g *sg.Graphics) { + g.Clear() + g.BeginAdditiveBlend() + //rl.ClearBackground(rl.Blank) + //rl.BeginBlendMode(rl.BlendAlphaPremultiply) + for x := range g.WidthInt32() { + for y := range g.HeightInt32() { v := fl.field.Get(float32(x), float32(y)) clr := LerpCurve(v, 1.3, fl.loColor, fl.hiColor) rl.DrawPixel(x, y, clr) diff --git a/internal/graphics/graphics.go b/internal/graphics/graphics.go index 853d6db..141b36a 100644 --- a/internal/graphics/graphics.go +++ b/internal/graphics/graphics.go @@ -12,17 +12,63 @@ type Graphics struct { Bounds Rect } +func CreateGraphics(bounds Rect) *Graphics { + return &Graphics { + Bounds: bounds, + Style: Style { + Fill: false, + FillColor: rl.RayWhite, + Stroke: true, + StrokeColor: rl.Black, + StrokeWeight: 1.0, + }, + } +} + type Style struct { StrokeColor, FillColor color.RGBA StrokeWeight float32 Stroke, Fill bool } -func (g *Graphics) GetGraphicsWidth() int32 { +func (g *Graphics) Center() Point { + return g.Bounds.Center() +} + +func (g *Graphics) Begin() { + rl.BeginBlendMode(rl.BlendAlphaPremultiply) + g.PushMatrix() + g.Translate(g.Bounds.UL()) + g.BeginClip(g.Bounds) +} + +func (g *Graphics) Clear() { + rl.ClearBackground(rl.Blank) +} + +func (g *Graphics) Background(c color.RGBA) { + rl.ClearBackground(c) +} + +func (g *Graphics) End() { + g.EndBlend() + g.EndClip() + g.PopMatrix() +} + +func (g *Graphics) Width() float32 { + return g.Bounds.Width +} + +func (g *Graphics) Height() float32 { + return g.Bounds.Height +} + +func (g *Graphics) WidthInt32() int32 { return int32(g.Bounds.Width) } -func (g *Graphics) GetGraphicsHeight() int32 { +func (g *Graphics) HeightInt32() int32 { return int32(g.Bounds.Height) } @@ -33,11 +79,11 @@ func (g *Graphics) PushStyle() { func (g *Graphics) PopStyle() { } -func (g *Graphics) SetStrokeColor(c Color) { +func (g *Graphics) SetStrokeColor(c color.RGBA) { g.Style.StrokeColor = color.RGBA { R: c.R, G: c.G, B: c.B, A: c.A } } -func (g *Graphics) SetFillColor(c Color) { +func (g *Graphics) SetFillColor(c color.RGBA) { g.Style.FillColor = color.RGBA { R: c.R, G: c.G, B: c.B, A: c.A } } @@ -87,6 +133,16 @@ func (g *Graphics) BeginTexture(t rl.RenderTexture2D) { rl.BeginTextureMode(t) } + +func (g *Graphics) DrawTexture(t rl.Texture2D, p Point, c color.RGBA) { + rl.DrawTexture(t, int32(p.X), int32(p.Y), c) +} + + +func (g *Graphics) TransferTexture(t rl.Texture2D, src Rect, dst Rect, tint color.RGBA) { + rl.DrawTexturePro(t, src.ToRL(), dst.ToRL(), rl.Vector2{}, 0, tint) +} + func (g *Graphics) EndTexture() { rl.EndTextureMode() } @@ -114,22 +170,25 @@ func (g *Graphics) DrawLine(a, b Point) { rl.SetLineWidth(saveLineWidth) } -type Color color.RGBA - -func RGBA(r, g, b, a uint8) Color { - return Color { R: r, G: g, B: b, A: a } -} - type HSBA struct { H uint S, B float32 A uint8 } +var Origin = Point { X: 0, Y: 0, Z: 0 } + type Point rl.Vector3 type Vec rl.Vector3 type Rect rl.Rectangle +func (r *Rect) Center() Point { + return Point { + X: r.X + r.Width / 2, + Y: r.X + r.Height / 2, + } +} + func (p *Point) Add(v Vec) Point { return Point { X: p.X + v.X, Y: p.Y + v.Y, Z: p.Z + v.Z } } diff --git a/main.go b/main.go index 4843db7..5be2df6 100644 --- a/main.go +++ b/main.go @@ -17,8 +17,7 @@ import ( //"github.com/ojrac/opensimplex-go" ) - -func Bootstrap() sg.Graphics { +func Bootstrap() *Env { rl.InitWindow(800, 600, "bootstrap") @@ -48,6 +47,8 @@ func Bootstrap() sg.Graphics { defaultWindowWidth, defaultWindowHeight, defaultGraphicsWidth, defaultWindowHeight) + var snapshotsPath string + flag.StringVar(&snapshotsPath, "path", "snapshots", "Path to snapshots and db") flag.IntVar(&graphicsWidth, "gw", defaultGraphicsWidth, "Width of the internal graphics buffer. Can be much larger than the screen.") flag.IntVar(&graphicsHeight, "gh", defaultGraphicsHeight, "Height of the internal graphics buffer. Can be much larger than the screen.") @@ -64,50 +65,47 @@ func Bootstrap() sg.Graphics { os.MkdirAll(snapshotsPath, 0755) var err error - storage, err = NewStorage(snapshotsPath) + storage, err := NewStorage(snapshotsPath) if err != nil { log.Printf("Error loading storage: %v\n", err) os.Exit(1) } - layout := sg.Layout { - Monitor: sg.Rect{X: 0, Y: 0, Width: float32(monitorWidth), Height: float32(monitorHeight)}, - Window: sg.Rect{X: 0, Y: 0, Width: float32(windowWidth), Height: float32(windowHeight)}, - Controls: sg.Rect{X: 0, Y: 0, Width: float32(controlsWidth), Height: float32(windowHeight)}, - Viewport: sg.Rect{X: float32(controlsWidth), Y: 0, Width: float32(viewportWidth), Height: float32(windowHeight)}, - Graphics: sg.Rect{X: 0, Y: 0, Width: float32(graphicsWidth), Height: float32(graphicsHeight)}, + layout := Layout{ + Monitor: sg.Rect{X: 0, Y: 0, Width: float32(monitorWidth), Height: float32(monitorHeight)}, + Window: sg.Rect{X: 0, Y: 0, Width: float32(windowWidth), Height: float32(windowHeight)}, + Controls: sg.Rect{X: 0, Y: 0, Width: float32(controlsWidth), Height: float32(windowHeight)}, + Viewport: sg.Rect{X: float32(controlsWidth), Y: 0, Width: float32(viewportWidth), Height: float32(windowHeight)}, + Offscreen: sg.Rect{X: 0, Y: 0, Width: float32(graphicsWidth), Height: float32(graphicsHeight)}, } //rl.SetConfigFlags(rl.FlagMsaa4xHint) rl.InitWindow(int32(layout.Window.Width), int32(layout.Window.Height), "sumi sierpinski arrow") rl.SetTargetFPS(60) - return sg.Graphics { - Layout: layout, - Style: sg.Style { - Fill: false, - FillColor: rl.RayWhite, - Stroke: true, - StrokeColor: rl.Black, - StrokeWeight: 1.0, - }, - } + env := NewEnv() + env.Layout = layout + env.Window = sg.CreateGraphics(layout.Window) + env.Viewport = sg.CreateGraphics(layout.Viewport) + env.Offscreen = sg.CreateGraphics(layout.Offscreen) + env.Controls = sg.CreateGraphics(layout.Controls) + env.Storage = storage + + return env } func main() { log := log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - g := Bootstrap() + env := Bootstrap() // reproducable flourescent color cycle colorCycle := sg.NewFixedColorCycle(sg.FlourescentColors).Shuffle(0) - t0 := time.Now() - - rng := rand.New(rand.NewSource(0)) + rng := rand.New(rand.NewSource(env.Time.Unix())) //imageField := NewImageField("/home/d/Dropbox/art/data/david.png") - noiseField := &SimplexNoiseField{Noise: opensimplex.New32(0)} + noiseField := &SimplexNoiseField{Noise: opensimplex.New32(env.Time.Unix())} sinXYField := &SinXYField{} //imageField := NewImageField("/home/d/Dropbox/art/data/ramstatue.png") //imageField := NewImageField("/home/d/Dropbox/art/data/bassrockastro/Photo Dec 24 2025, 5 58 23 PM.jpg") @@ -115,8 +113,8 @@ func main() { //imageField := NewImageField("/home/d/Dropbox/art/data/moses_statue.jpg") field := &TranslateField{ - x: -float32(g.Layout.Graphics.Width / 2.0), - y: -float32(g.Layout.Graphics.Height / 2.0), + x: -float32(env.Offscreen.Bounds.Width / 2.0), + y: -float32(env.Offscreen.Bounds.Height / 2.0), field: &ScaleField{ scale: 100.0, field: &AdderField{ @@ -130,7 +128,7 @@ func main() { //sierpinskiLayer := &SierpinskiArrow { dirty: true } - sketch := NewSketch(&g) + sketch := NewSketch(env) fieldColor := colorCycle.Next() fmt.Printf("field color = %v\n", fieldColor) @@ -148,11 +146,10 @@ func main() { actorColor = sg.Clamp(actorColor, 10, 255) actorColor.A = 10 - actorSGColor := sg.Color { R: actorColor.R, G: actorColor.G, B: actorColor.B, A: actorColor.A } //NewColor(11, 35, 176, 50), //r - contourLayer := NewContourLayer(&sketch, rng, field, actorSGColor, -25*math.Pi, 25*math.Pi) + contourLayer := NewContourLayer(&sketch, rng, field, actorColor, -25*math.Pi, 25*math.Pi) sketch.AddLayer("contours", contourLayer) //sketch.AddLayer("sierpinski-arrowhead", sierpinskiLayer) // aurora := NewImageLayer("/home/d/Dropbox/photos/Events/2025/Aurora/Photo Nov 11 2025, 9 52 03 PM.jpg") @@ -181,14 +178,10 @@ func main() { for !rl.WindowShouldClose() { // begin drawing - t := time.Since(t0).Seconds() + t := time.Since(env.Time).Seconds() - // set up RenderCtx - env := &Env { - Graphics: g, - Time: t, - Ports: ports.Eval(t), - } + env.Time = time.Now() + env.Ports = ports.Eval(t) sketch.Update(env) @@ -267,7 +260,7 @@ func main() { for ch := rl.GetCharPressed(); ch != 0; ch = rl.GetCharPressed() { c := rune(ch) if c == 'c' { - sketch.ResetCamera() + sketch.ResetCamera(env) } else if c >= '1' && c <= '9' { zoom := 1 << int(ch-'0') sketch.cam.Zoom = float32(zoom) diff --git a/sketch.go b/sketch.go index 870447a..b142954 100644 --- a/sketch.go +++ b/sketch.go @@ -8,7 +8,7 @@ import ( ) type Sketch struct { - graphics *sg.Graphics + env *Env cam *TextureCam composite rl.RenderTexture2D layerTools map[string]*LayerTools @@ -42,26 +42,26 @@ type LayerConfig struct { kValue float32 } -func NewSketch(g *sg.Graphics) Sketch { +func NewSketch(env *Env) Sketch { // point at source center // put source center at center of screen var camera = TextureCam{ - LookAt: rl.Vector2{X: float32(g.Layout.Graphics.Width) / 2.0, Y: float32(g.Layout.Graphics.Height) / 2.0}, + LookAt: rl.Vector2{X: env.Offscreen.Width() / 2.0, Y: env.Offscreen.Height() / 2.0}, Zoom: 1.0, } return Sketch{ - graphics: g, + env: env, layerTools: make(map[string]*LayerTools), layerToolsOrdered: []*LayerTools{}, - composite: rl.LoadRenderTexture(int32(g.Layout.Graphics.Width), int32(g.Layout.Graphics.Height)), + composite: rl.LoadRenderTexture(env.Offscreen.WidthInt32(), env.Offscreen.HeightInt32()), cam: &camera, } } func (s *Sketch) AddLayer(name string, layer Layer) { - texture := rl.LoadRenderTexture(int32(s.graphics.Layout.Graphics.Width), int32(s.graphics.Layout.Graphics.Height)) + texture := rl.LoadRenderTexture(s.env.Offscreen.WidthInt32(), s.env.Offscreen.HeightInt32()) config := NewLayerConfig() layerTools := LayerTools{ @@ -82,87 +82,101 @@ func (s *Sketch) AddColorLayer(name string, c rl.Color) { s.AddLayer(name, colorLayer) } -func (s *Sketch) Redraw(ctx *Env) { - g := ctx.Graphics +func (s *Sketch) RedrawLayers(env *Env, g *sg.Graphics) { // render onto all layer textures for _, instance := range s.layerToolsOrdered { layer := instance.layer // ignore this layer entirely unless it's visible if instance.config.visible { - layer.Update(ctx) + w := float32(instance.texture.Texture.Width) + h := float32(instance.texture.Texture.Height) + lg := sg.CreateGraphics(sg.Rect { X: 0, Y: 0, Width: w, Height: h }) + layer.Update(env, g) // re-render to texture if dirty if instance.layer.IsDirty() { - g.PushMatrix() - g.BeginTexture(instance.texture) - layer.Draw(ctx) - g.PopMatrix() - g.EndTexture() + lg.Begin() + lg.BeginTexture(instance.texture) + layer.Draw(env, lg) + lg.EndTexture() + lg.End() } } } } -func (s *Sketch) Draw(ctx *Env) { +func (s *Sketch) Draw(env *Env) { - g := ctx.Graphics - s.Redraw(ctx) + offscreen := env.Offscreen + s.RedrawLayers(env, offscreen) // copy from full texture for compositing, with vertical flipping - src := g.Layout.Graphics + src := offscreen.Bounds src.Height = -src.Height - dst := g.Layout.Graphics + dst := offscreen.Bounds /* - src := g.Rect { - X: 0, Y: 0, - Width: float32(ctx.SourceWidth), - Height: -float32(ctx.SourceHeight), - } - dst := g.Rect{ - X: 0, Y: 0, - Width: float32(ctx.SourceWidth), - Height: float32(ctx.SourceHeight), - } + src := g.Rect { + X: 0, Y: 0, + Width: float32(ctx.SourceWidth), + Height: -float32(ctx.SourceHeight), + } + dst := g.Rect{ + X: 0, Y: 0, + Width: float32(ctx.SourceWidth), + Height: float32(ctx.SourceHeight), + } */ - viewport := s.CalcViewport(ctx) + offscreen.Begin() - outputRect := g.Layout.Graphics.ScaleTo(g.Layout.Viewport) + // calculate the viewable region of the offscreen buffer + viewport := s.CalcViewport(env) + + // scale the offscreen buffer to the viewport maintaining aspect ratio + outputRect := offscreen.Bounds.ScaleTo(env.Viewport.Bounds) fmt.Printf("outputRect = %v\n", outputRect) x := float32(0) y := float32(0) w := outputRect.Width h := outputRect.Height - g.PushMatrix() - g.Translate(outputRect.UL()) - g.BeginClip(outputRect) - checkSize := float32(25.0) - grey := rl.NewColor(220, 220, 220, 255) + + output := sg.CreateGraphics(outputRect) + + output.Begin() + output.SetFill(true) + output.SetStroke(false) + checkSize := float32(outputRect.Width / 30.0) + grey := sg.RGBA(220, 220, 220, 255) cellX := 0 cellY := 0 for y < h { x = 0 cellX = 0 for x < w { - c := rl.White + c := sg.RGBA(rl.White.R, rl.White.G, rl.White.B, rl.White.A) if ((cellX + cellY) & 1) == 1 { c = grey } - rl.DrawRectangle(int32(x), int32(y), int32(checkSize), int32(checkSize), c) + output.SetFillColor(c) + output.DrawRect(sg.Rect { X: x, Y: y, Width: checkSize, Height: checkSize }) + //rl.DrawRectangle(int32(x), int32(y), int32(checkSize), int32(checkSize), c) x += checkSize cellX++ } y += checkSize cellY++ } - g.EndClip() - g.PopMatrix() - rl.BeginBlendMode(rl.BlendAlphaPremultiply) + // render each layer onto the output graphics context + offscreen.BeginTexture(s.composite) + offscreen.Clear() + + //rl.BeginBlendMode(rl.BlendAlphaPremultiply) //rl.BeginBlendMode(rl.BlendAlpha) - rl.BeginTextureMode(s.composite) - rl.ClearBackground(rl.Blank) + //rl.BeginTextureMode(s.composite) + + //rl.ClearBackground(rl.Blank) //rl.ClearBackground(rl.Black) for _, instance := range s.layerToolsOrdered { config := instance.config @@ -183,39 +197,44 @@ func (s *Sketch) Draw(ctx *Env) { g = uint8(float32(g) * (float32(config.a) / 255.0)) b = uint8(float32(b) * (float32(config.a) / 255.0)) tint := rl.NewColor(r, g, b, config.a) - rl.DrawTexturePro(instance.texture.Texture, src.ToRL(), dst.ToRL(), rl.Vector2{}, 0, tint) + offscreen.TransferTexture(instance.texture.Texture, src, dst, tint) + //rl.DrawTexturePro(instance.texture.Texture, src.ToRL(), dst.ToRL(), rl.Vector2{}, 0, tint) } } - rl.EndTextureMode() - rl.EndBlendMode() + offscreen.EndTexture() + offscreen.End() rl.GenTextureMipmaps(&s.composite.Texture) rl.SetTextureFilter(s.composite.Texture, rl.FilterTrilinear) - rl.DrawTexturePro(s.composite.Texture, viewport.ToRL(), outputRect.ToRL(), rl.Vector2{}, 0, rl.White) + output.TransferTexture(s.composite.Texture, viewport, outputRect, rl.White) - outputRectRL := outputRect.ToRL() - outlineRect := (&outputRectRL).ToInt32() - rl.DrawRectangleLines(outlineRect.X, outlineRect.Y, outlineRect.Width, outlineRect.Height, rl.Gray) + output.SetFill(false) + output.SetStroke(true) + output.SetStrokeColor(rl.Gray) + output.DrawRect(outputRect) + + output.End() } -func (s *Sketch) CalcViewport(ctx *Env) sg.Rect { - viewportWidth := rl.Clamp(float32(ctx.Graphics.Layout.Graphics.Width)/s.cam.Zoom, 0, float32(ctx.Graphics.Layout.Graphics.Width)) - viewportHeight := rl.Clamp(float32(ctx.Graphics.Layout.Graphics.Height)/s.cam.Zoom, 0, float32(ctx.Graphics.Layout.Graphics.Height)) +// calculate the visible clip of the offscreen buffer based on the camera /zoom +func (s *Sketch) CalcViewport(env *Env) sg.Rect { + viewportWidth := rl.Clamp(env.Offscreen.Width()/s.cam.Zoom, 0, env.Offscreen.Width()) + viewportHeight := rl.Clamp(env.Offscreen.Height()/s.cam.Zoom, 0, env.Offscreen.Width()) return sg.Rect{ - X: rl.Clamp(s.cam.LookAt.X-viewportWidth/2.0, 0, float32(ctx.Graphics.Layout.Graphics.Width)-viewportWidth), - Y: rl.Clamp(s.cam.LookAt.Y-viewportHeight/2.0, 0, float32(ctx.Graphics.Layout.Graphics.Height)-viewportHeight), - Width: float32(viewportWidth), - Height: -float32(viewportHeight), + X: rl.Clamp(s.cam.LookAt.X-viewportWidth/2.0, 0, env.Layout.Offscreen.Width-viewportWidth), + Y: rl.Clamp(s.cam.LookAt.Y-viewportHeight/2.0, 0, env.Layout.Offscreen.Height-viewportHeight), + Width: viewportWidth, + Height: -viewportHeight, } } -func (s *Sketch) Update(ctx *Env) { +func (s *Sketch) Update(env *Env) { if rl.IsMouseButtonDown(rl.MouseRightButton) { // get mouse delta from last frame delta := rl.GetMouseDelta() - sourceScale := float32(ctx.Graphics.Layout.Graphics.Width) / float32(ctx.Graphics.Layout.Viewport.Width) + sourceScale := env.Offscreen.Width() / env.Viewport.Width() // compute the amount to move scaled by the camera zoom delta = rl.Vector2Scale(delta, -sourceScale/s.cam.Zoom) delta.Y = -delta.Y @@ -223,8 +242,8 @@ func (s *Sketch) Update(ctx *Env) { } // clamp LookAt to be somewhere on the texture - s.cam.LookAt.X = rl.Clamp(s.cam.LookAt.X, 0, float32(ctx.Graphics.Layout.Graphics.Width)) - s.cam.LookAt.Y = rl.Clamp(s.cam.LookAt.Y, 0, float32(ctx.Graphics.Layout.Graphics.Height)) + s.cam.LookAt.X = rl.Clamp(s.cam.LookAt.X, 0, env.Offscreen.Width()) + s.cam.LookAt.Y = rl.Clamp(s.cam.LookAt.Y, 0, env.Offscreen.Height()) // Zoom based on mouse wheel wheel := rl.GetMouseWheelMove() @@ -241,8 +260,12 @@ func (s *Sketch) Update(ctx *Env) { s.cam.Zoom = rl.Clamp(s.cam.Zoom, 1, math.MaxInt64) } -func (s *Sketch) ResetCamera() { - s.cam.LookAt = rl.Vector2{X: float32(s.graphics.Layout.Graphics.Width) / 2.0, Y: float32(s.graphics.Layout.Graphics.Height) / 2.0} +func (s *Sketch) ResetCamera(env *Env) { + s.cam.LookAt = + rl.Vector2 { + X: env.Offscreen.Width() / 2.0, + Y: env.Offscreen.Height() / 2.0, + } s.cam.Zoom = 1.0 } @@ -253,7 +276,7 @@ type SketchCapture struct { layerToolsOrdered []*LayerTools } -func (s *Sketch) Capture() *SketchCapture { +func (s *Sketch) Capture(env *Env) *SketchCapture { composite := rl.LoadImageFromTexture(s.composite.Texture) rl.ImageFlipVertical(composite) for _, layerTool := range s.layerToolsOrdered { @@ -261,8 +284,8 @@ func (s *Sketch) Capture() *SketchCapture { rl.ImageFlipVertical(layerTool.capture) } return &SketchCapture{ - width: s.graphics.GetGraphicsWidth(), - height: s.graphics.GetGraphicsHeight(), + width: env.Offscreen.WidthInt32(), + height: env.Offscreen.HeightInt32(), compositeImage: composite, layerTools: s.layerTools, layerToolsOrdered: s.layerToolsOrdered, @@ -287,8 +310,8 @@ func NewLayerConfig() LayerConfig { /** Layer **/ type Layer interface { - Update(ctx *Env) - Draw(ctx *Env) + Update(ctx *Env, g *sg.Graphics) + Draw(ctx *Env, g *sg.Graphics) IsDirty() bool } @@ -297,12 +320,13 @@ type ColorLayer struct { dirty bool } -func (cl *ColorLayer) Update(ctx *Env) { +func (cl *ColorLayer) Update(ctx *Env, g *sg.Graphics) { } -func (cl *ColorLayer) Draw(ctx *Env) { - rl.ClearBackground(cl.color) +func (cl *ColorLayer) Draw(ctx *Env, g *sg.Graphics) { + g.Background(cl.color) + //rl.ClearBackground(cl.color) cl.dirty = false } @@ -324,55 +348,21 @@ func NewImageLayer(path string) *ImageLayer { } } -func (il *ImageLayer) Update(ctx *Env) { +func (il *ImageLayer) Update(ctx *Env, g *sg.Graphics) { } -func (il *ImageLayer) Draw(ctx *Env) { - rl.Translatef(float32(ctx.GetGraphicsWidth())/2.0-float32(il.texture.Width)/2.0, float32(ctx.GetGraphicsHeight())/2.0-float32(il.texture.Height)/2.0, 0) - rl.DrawTexture(il.texture, 0, 0, rl.White) +func (il *ImageLayer) Draw(env *Env, g *sg.Graphics) { + rl.Translatef( + g.Width()/2.0-float32(il.texture.Width)/2.0, + g.Width()/2.0-float32(il.texture.Height)/2.0, 0) + g.DrawTexture(il.texture, sg.Origin, rl.White) } func (il *ImageLayer) IsDirty() bool { return il.dirty } -type TestPattern struct { - dirty bool -} - -func (tp *TestPattern) Update(ctx *Env) { - -} - -func (tp *TestPattern) Draw(ctx *Env) { - - rl.ClearBackground(rl.Black) - centerX := float32(ctx.GetGraphicsWidth()) / 2 - centerY := float32(ctx.GetGraphicsHeight()) / 2 - - rl.DrawRectangleRec(rl.Rectangle{X: 0, Y: 0, Width: centerX, Height: centerY}, rl.Red) - rl.DrawRectangleRec(rl.Rectangle{X: centerX, Y: 0, Width: centerX, Height: centerY}, rl.Green) - rl.DrawRectangleRec(rl.Rectangle{X: 0, Y: centerY, Width: centerX, Height: centerY}, rl.Blue) - rl.DrawRectangleRec(rl.Rectangle{X: centerX, Y: centerY, Width: centerX, Height: centerY}, rl.White) - - rl.DrawLine(0, 0, ctx.Graphics.GetGraphicsWidth(), ctx.Graphics.GetGraphicsHeight(), rl.Black) - - rl.PushMatrix() - rl.Translatef(centerX, centerY, 0) - rl.SetLineWidth(4.0) - rl.DrawLine(-10000, 0, 10000, 0, rl.Red) - rl.DrawLine(0, -10000, 0, 10000, rl.Green) - rl.DrawRectangleLines(-50, -50, 100, 100, rl.Magenta) - rl.PopMatrix() - - tp.dirty = false -} - -func (tp *TestPattern) IsDirty() bool { - return tp.dirty -} - /** Ports **/ type Ports map[string]Signal