diff --git a/geometry.go b/geometry.go index 13a574d..02a8b51 100644 --- a/geometry.go +++ b/geometry.go @@ -49,6 +49,11 @@ func (u Vec) XY() (x, y float64) { return u.X, u.Y } +// Normal returns a vector normal to u (rotated by math.pi/2) +func (u Vec) Normal() Vec { + return Vec{X: u.Y, Y: -u.X} +} + // Add returns the sum of vectors u and v. func (u Vec) Add(v Vec) Vec { return Vec{ @@ -65,6 +70,14 @@ func (u Vec) Sub(v Vec) Vec { } } +// To returns the vector from vector u to vector v, equivalent to v.Sub(u). +func (u Vec) To(v Vec) Vec { + return Vec{ + v.X - u.X, + v.Y - u.Y, + } +} + // Scaled returns the vector u multiplied by c. func (u Vec) Scaled(c float64) Vec { return Vec{u.X * c, u.Y * c} diff --git a/imdraw/imdraw.go b/imdraw/imdraw.go index f7ce769..e616c39 100644 --- a/imdraw/imdraw.go +++ b/imdraw/imdraw.go @@ -128,7 +128,9 @@ func (imd *IMDraw) Draw(t pixel.Target) { // Push adds some points to the IM queue. All Pushed points will have the same properties except for // the position. func (imd *IMDraw) Push(pts ...pixel.Vec) { - imd.Color = pixel.ToRGBA(imd.Color) + if _, ok := imd.Color.(pixel.RGBA); !ok { + imd.Color = pixel.ToRGBA(imd.Color) + } opts := point{ col: imd.Color.(pixel.RGBA), pic: imd.Picture, @@ -495,12 +497,12 @@ func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness floa thick := pixel.V(thickness/2, 0).Rotated(normalLow) imd.pushPt(lowCenter.Add(thick), pt) imd.pushPt(lowCenter.Sub(thick), pt) - imd.pushPt(lowCenter.Sub(thick.Rotated(math.Pi/2*orientation)), pt) + imd.pushPt(lowCenter.Sub(thick.Normal().Scaled(orientation)), pt) imd.fillPolygon() thick = pixel.V(thickness/2, 0).Rotated(normalHigh) imd.pushPt(highCenter.Add(thick), pt) imd.pushPt(highCenter.Sub(thick), pt) - imd.pushPt(highCenter.Add(thick.Rotated(math.Pi/2*orientation)), pt) + imd.pushPt(highCenter.Add(thick.Normal().Scaled(orientation)), pt) imd.fillPolygon() case RoundEndShape: imd.pushPt(lowCenter, pt) @@ -528,29 +530,27 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { // first point j, i := 0, 1 - normal := points[i].pos.Sub(points[j].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) + ijNormal := points[0].pos.To(points[1].pos).Normal().Unit().Scaled(thickness / 2) if !closed { switch points[j].endshape { case NoEndShape: // nothing case SharpEndShape: - imd.pushPt(points[j].pos.Add(normal), points[j]) - imd.pushPt(points[j].pos.Sub(normal), points[j]) - imd.pushPt(points[j].pos.Add(normal.Rotated(math.Pi/2)), points[j]) + imd.pushPt(points[j].pos.Add(ijNormal), points[j]) + imd.pushPt(points[j].pos.Sub(ijNormal), points[j]) + imd.pushPt(points[j].pos.Add(ijNormal.Normal()), points[j]) imd.fillPolygon() case RoundEndShape: imd.pushPt(points[j].pos, points[j]) - imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normal.Angle(), normal.Angle()+math.Pi) + imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()+math.Pi) } } - imd.pushPt(points[j].pos.Add(normal), points[j]) - imd.pushPt(points[j].pos.Sub(normal), points[j]) + imd.pushPt(points[j].pos.Add(ijNormal), points[j]) + imd.pushPt(points[j].pos.Sub(ijNormal), points[j]) // middle points - // compute "previous" normal: - ijNormal := points[1].pos.Sub(points[0].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) for i := 0; i < len(points); i++ { j, k := i+1, i+2 @@ -566,7 +566,7 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { k %= len(points) } - jkNormal := points[k].pos.Sub(points[j].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) + jkNormal := points[j].pos.To(points[k].pos).Normal().Unit().Scaled(thickness / 2) orientation := 1.0 if ijNormal.Cross(jkNormal) > 0 { @@ -602,10 +602,10 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { // last point i, j = len(points)-2, len(points)-1 - normal = points[j].pos.Sub(points[i].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) + ijNormal = points[i].pos.To(points[j].pos).Normal().Unit().Scaled(thickness / 2) - imd.pushPt(points[j].pos.Sub(normal), points[j]) - imd.pushPt(points[j].pos.Add(normal), points[j]) + imd.pushPt(points[j].pos.Sub(ijNormal), points[j]) + imd.pushPt(points[j].pos.Add(ijNormal), points[j]) imd.fillPolygon() if !closed { @@ -613,13 +613,13 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { case NoEndShape: // nothing case SharpEndShape: - imd.pushPt(points[j].pos.Add(normal), points[j]) - imd.pushPt(points[j].pos.Sub(normal), points[j]) - imd.pushPt(points[j].pos.Add(normal.Rotated(-math.Pi/2)), points[j]) + imd.pushPt(points[j].pos.Add(ijNormal), points[j]) + imd.pushPt(points[j].pos.Sub(ijNormal), points[j]) + imd.pushPt(points[j].pos.Add(ijNormal.Normal().Scaled(-1)), points[j]) imd.fillPolygon() case RoundEndShape: imd.pushPt(points[j].pos, points[j]) - imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normal.Angle(), normal.Angle()-math.Pi) + imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()-math.Pi) } } diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index b8e4057..9e1efeb 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -380,7 +380,7 @@ var canvasUniformFormat = glhf.AttrFormat{ } var canvasVertexShader = ` -#version 330 core +#version 130 in vec2 position; in vec4 color; @@ -405,7 +405,7 @@ void main() { ` var canvasFragmentShader = ` -#version 330 core +#version 130 in vec4 Color; in vec2 TexCoords; diff --git a/pixelgl/gltriangles.go b/pixelgl/gltriangles.go index bf7a895..52ed0c9 100644 --- a/pixelgl/gltriangles.go +++ b/pixelgl/gltriangles.go @@ -60,9 +60,10 @@ func (gt *GLTriangles) Len() int { // SetLen efficiently resizes GLTriangles to len. // // Time complexity is amortized O(1). -func (gt *GLTriangles) SetLen(len int) { - if len > gt.Len() { - needAppend := len - gt.Len() +func (gt *GLTriangles) SetLen(length int) { + switch { + case length > gt.Len(): + needAppend := length - gt.Len() for i := 0; i < needAppend; i++ { gt.data = append(gt.data, 0, 0, @@ -71,11 +72,16 @@ func (gt *GLTriangles) SetLen(len int) { 0, ) } + case length < gt.Len(): + gt.data = gt.data[:length*gt.vs.Stride()] + default: + return } - if len < gt.Len() { - gt.data = gt.data[:len*gt.vs.Stride()] - } - gt.submitData() + mainthread.CallNonBlock(func() { + gt.vs.Begin() + gt.vs.SetLen(length) + gt.vs.End() + }) } // Slice returns a sub-Triangles of this GLTriangles in range [i, j). @@ -95,16 +101,17 @@ func (gt *GLTriangles) updateData(t pixel.Triangles) { } // TrianglesData short path + stride := gt.vs.Stride() + length := gt.Len() if t, ok := t.(*pixel.TrianglesData); ok { - for i := 0; i < gt.Len(); i++ { + for i := 0; i < length; i++ { var ( px, py = (*t)[i].Position.XY() col = (*t)[i].Color tx, ty = (*t)[i].Picture.XY() in = (*t)[i].Intensity ) - s := gt.vs.Stride() - d := gt.data[i*s : i*s+9] + d := gt.data[i*stride : i*stride+9] d[0] = float32(px) d[1] = float32(py) d[2] = float32(col.R) @@ -119,54 +126,31 @@ func (gt *GLTriangles) updateData(t pixel.Triangles) { } if t, ok := t.(pixel.TrianglesPosition); ok { - for i := 0; i < gt.Len(); i++ { + for i := 0; i < length; i++ { px, py := t.Position(i).XY() - gt.data[i*gt.vs.Stride()+0] = float32(px) - gt.data[i*gt.vs.Stride()+1] = float32(py) + gt.data[i*stride+0] = float32(px) + gt.data[i*stride+1] = float32(py) } } if t, ok := t.(pixel.TrianglesColor); ok { - for i := 0; i < gt.Len(); i++ { + for i := 0; i < length; i++ { col := t.Color(i) - gt.data[i*gt.vs.Stride()+2] = float32(col.R) - gt.data[i*gt.vs.Stride()+3] = float32(col.G) - gt.data[i*gt.vs.Stride()+4] = float32(col.B) - gt.data[i*gt.vs.Stride()+5] = float32(col.A) + gt.data[i*stride+2] = float32(col.R) + gt.data[i*stride+3] = float32(col.G) + gt.data[i*stride+4] = float32(col.B) + gt.data[i*stride+5] = float32(col.A) } } if t, ok := t.(pixel.TrianglesPicture); ok { - for i := 0; i < gt.Len(); i++ { + for i := 0; i < length; i++ { pic, intensity := t.Picture(i) - gt.data[i*gt.vs.Stride()+6] = float32(pic.X) - gt.data[i*gt.vs.Stride()+7] = float32(pic.Y) - gt.data[i*gt.vs.Stride()+8] = float32(intensity) + gt.data[i*stride+6] = float32(pic.X) + gt.data[i*stride+7] = float32(pic.Y) + gt.data[i*stride+8] = float32(intensity) } } } -func (gt *GLTriangles) submitData() { - // this code is supposed to copy the vertex data and CallNonBlock the update if - // the data is small enough, otherwise it'll block and not copy the data - if len(gt.data) < 256 { // arbitrary heurestic constant - data := append([]float32{}, gt.data...) - mainthread.CallNonBlock(func() { - gt.vs.Begin() - dataLen := len(data) / gt.vs.Stride() - gt.vs.SetLen(dataLen) - gt.vs.SetVertexData(data) - gt.vs.End() - }) - } else { - mainthread.Call(func() { - gt.vs.Begin() - dataLen := len(gt.data) / gt.vs.Stride() - gt.vs.SetLen(dataLen) - gt.vs.SetVertexData(gt.data) - gt.vs.End() - }) - } -} - // Update copies vertex properties from the supplied Triangles into this GLTriangles. // // The two Triangles (gt and t) must be of the same len. @@ -175,7 +159,23 @@ func (gt *GLTriangles) Update(t pixel.Triangles) { panic(fmt.Errorf("(%T).Update: invalid triangles len", gt)) } gt.updateData(t) - gt.submitData() + + // this code is supposed to copy the vertex data and CallNonBlock the update if + // the data is small enough, otherwise it'll block and not copy the data + if len(gt.data) < 256 { // arbitrary heurestic constant + data := append([]float32{}, gt.data...) + mainthread.CallNonBlock(func() { + gt.vs.Begin() + gt.vs.SetVertexData(data) + gt.vs.End() + }) + } else { + mainthread.Call(func() { + gt.vs.Begin() + gt.vs.SetVertexData(gt.data) + gt.vs.End() + }) + } } // Copy returns an independent copy of this GLTriangles. diff --git a/pixelgl/window.go b/pixelgl/window.go index afbd5f7..76523a5 100644 --- a/pixelgl/window.go +++ b/pixelgl/window.go @@ -90,10 +90,8 @@ func NewWindow(cfg WindowConfig) (*Window, error) { err := mainthread.CallErr(func() error { var err error - glfw.WindowHint(glfw.ContextVersionMajor, 3) - glfw.WindowHint(glfw.ContextVersionMinor, 3) - glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) - glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) + glfw.WindowHint(glfw.ContextVersionMajor, 2) + glfw.WindowHint(glfw.ContextVersionMinor, 1) glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable]) glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])