Files
2026-09-26 16:32:40 +03:00

199 lines
4.4 KiB
Go

package debug
import (
"unsafe"
"aego/core/mathx"
"aego/gpu"
)
type Space uint8
const (
World Space = iota
Screen
)
const VertexStride = 12
type Vertex struct {
Pos mathx.Vec2
Color uint32
}
const _ = uint(VertexStride - unsafe.Sizeof(Vertex{}))
const _ = uint(unsafe.Sizeof(Vertex{}) - VertexStride)
type Draw struct {
dev gpu.Device
vb gpu.BufferHandle
max int
world []Vertex
screen []Vertex
space Space
dropped uint32
uploaded int
}
func New(dev gpu.Device, maxVerts int) (*Draw, error) {
if maxVerts < 2 {
maxVerts = 2
}
vb, err := dev.CreateBuffer(gpu.BufferDesc{
Usage: gpu.BufferVertex,
Size: maxVerts * VertexStride,
Dynamic: true,
Label: "debug.lines",
})
if err != nil {
return nil, err
}
return &Draw{
dev: dev,
vb: vb,
max: maxVerts,
world: make([]Vertex, 0, maxVerts/2),
screen: make([]Vertex, 0, maxVerts/2),
}, nil
}
func (d *Draw) Buffer() gpu.BufferHandle {
return d.vb
}
func (d *Draw) Reset() {
d.world = d.world[:0]
d.screen = d.screen[:0]
d.space = World
d.dropped = 0
d.uploaded = 0
}
func (d *Draw) Space(s Space) {
d.space = s
}
func (d *Draw) Dropped() uint32 {
return d.dropped
}
func (d *Draw) Counts() (world, screen int) {
return len(d.world), len(d.screen)
}
func (d *Draw) push(a, b mathx.Vec2, c mathx.Color) {
if len(d.world)+len(d.screen)+2 > d.max {
d.dropped += 2
return
}
packed := c.Premultiplied().PackRGBA8()
if d.space == Screen {
d.screen = append(d.screen, Vertex{Pos: a, Color: packed}, Vertex{Pos: b, Color: packed})
return
}
d.world = append(d.world, Vertex{Pos: a, Color: packed}, Vertex{Pos: b, Color: packed})
}
func (d *Draw) Line(a, b mathx.Vec2, c mathx.Color) {
d.push(a, b, c)
}
func (d *Draw) Rect(r mathx.Rect, c mathx.Color) {
p0 := mathx.Vec2{X: r.X, Y: r.Y}
p1 := mathx.Vec2{X: r.X + r.W, Y: r.Y}
p2 := mathx.Vec2{X: r.X + r.W, Y: r.Y + r.H}
p3 := mathx.Vec2{X: r.X, Y: r.Y + r.H}
d.push(p0, p1, c)
d.push(p1, p2, c)
d.push(p2, p3, c)
d.push(p3, p0, c)
}
func (d *Draw) AABB(b mathx.AABB, c mathx.Color) {
size := b.Size()
d.Rect(mathx.Rect{X: b.Min.X, Y: b.Min.Y, W: size.X, H: size.Y}, c)
}
func (d *Draw) RectRot(center, size mathx.Vec2, rad float32, c mathx.Color) {
hx, hy := size.X/2, size.Y/2
s, co := mathx.Sin(rad), mathx.Cos(rad)
rot := func(x, y float32) mathx.Vec2 {
return mathx.Vec2{X: center.X + x*co - y*s, Y: center.Y + x*s + y*co}
}
p0 := rot(-hx, -hy)
p1 := rot(hx, -hy)
p2 := rot(hx, hy)
p3 := rot(-hx, hy)
d.push(p0, p1, c)
d.push(p1, p2, c)
d.push(p2, p3, c)
d.push(p3, p0, c)
}
func (d *Draw) Circle(center mathx.Vec2, radius float32, segments int, c mathx.Color) {
if segments < 3 {
segments = 3
}
step := mathx.TwoPi / float32(segments)
prev := mathx.Vec2{X: center.X + radius, Y: center.Y}
for i := 1; i <= segments; i++ {
a := step * float32(i)
next := mathx.Vec2{X: center.X + radius*mathx.Cos(a), Y: center.Y + radius*mathx.Sin(a)}
d.push(prev, next, c)
prev = next
}
}
func (d *Draw) Cross(p mathx.Vec2, size float32, c mathx.Color) {
h := size / 2
d.push(mathx.Vec2{X: p.X - h, Y: p.Y}, mathx.Vec2{X: p.X + h, Y: p.Y}, c)
d.push(mathx.Vec2{X: p.X, Y: p.Y - h}, mathx.Vec2{X: p.X, Y: p.Y + h}, c)
}
func (d *Draw) Arrow(a, b mathx.Vec2, c mathx.Color) {
d.push(a, b, c)
dir := b.Sub(a)
l := dir.Len()
if l == 0 {
return
}
dir = dir.Scale(1 / l)
head := min(l*0.25, 16)
left := dir.Rotated(2.6).Scale(head)
right := dir.Rotated(-2.6).Scale(head)
d.push(b, b.Add(left), c)
d.push(b, b.Add(right), c)
}
func (d *Draw) Upload() (worldFirst, worldCount, screenFirst, screenCount int) {
total := len(d.world) + len(d.screen)
if total == 0 {
return 0, 0, 0, 0
}
if len(d.world) > 0 {
raw := unsafe.Slice((*byte)(unsafe.Pointer(&d.world[0])), len(d.world)*VertexStride)
d.dev.UpdateBuffer(d.vb, 0, raw)
}
if len(d.screen) > 0 {
raw := unsafe.Slice((*byte)(unsafe.Pointer(&d.screen[0])), len(d.screen)*VertexStride)
d.dev.UpdateBuffer(d.vb, len(d.world)*VertexStride, raw)
}
d.uploaded = total
return 0, len(d.world), len(d.world), len(d.screen)
}
func (d *Draw) Close() {
d.dev.DestroyBuffer(d.vb)
}
func Layout() gpu.VertexLayout {
return gpu.VertexLayout{
Attrs: []gpu.VertexAttr{
{Location: 0, Slot: 0, Format: gpu.VFFloat2, Offset: 0},
{Location: 1, Slot: 0, Format: gpu.VFUByte4Norm, Offset: 8},
},
Strides: [gpu.MaxVertexSlots]uint16{VertexStride},
}
}