From 8c15b57d585c4529757921f5f4a3fd42b26361ef Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 17 Jan 2015 12:45:19 +0900 Subject: [PATCH] Add image.DrawLine / DrawLines --- example/shapes/main.go | 6 +- graphics.go | 2 +- image.go | 21 +++++-- internal/graphics/framebuffer.go | 18 +++++- internal/graphics/internal/shader/draw.go | 52 ++++++++++++++-- internal/graphics/internal/shader/program.go | 64 ++++++++++++++++---- internal/graphics/internal/shader/shader.go | 4 +- internal/graphics/texture.go | 4 +- internal/opengl/context.go | 23 +++++-- internal/opengl/context_js.go | 30 ++++++--- internal/opengl/types.go | 15 +++-- shapes.go | 29 ++++++++- 12 files changed, 217 insertions(+), 51 deletions(-) diff --git a/example/shapes/main.go b/example/shapes/main.go index dbec15df1..c5b6dfeb4 100644 --- a/example/shapes/main.go +++ b/example/shapes/main.go @@ -26,8 +26,10 @@ const ( ) func update(screen *ebiten.Image) error { - screen.DrawRect(10, 10, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80}) - screen.DrawRect(20, 20, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80}) + screen.FillRect(10, 10, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80}) + screen.FillRect(20, 20, 100, 100, color.NRGBA{0x80, 0x80, 0xff, 0x80}) + screen.DrawLine(130, 10, 140, 150, color.NRGBA{0xff, 0x80, 0x80, 0x80}) + screen.DrawLine(140, 10, 150, 150, color.NRGBA{0xff, 0x80, 0x80, 0x80}) return nil } diff --git a/graphics.go b/graphics.go index 55a3822c6..04df95954 100644 --- a/graphics.go +++ b/graphics.go @@ -27,7 +27,7 @@ const ( FilterLinear ) -func glFilter(c *opengl.Context, filter Filter) opengl.FilterType { +func glFilter(c *opengl.Context, filter Filter) opengl.Filter { switch filter { case FilterNearest: return c.Nearest diff --git a/image.go b/image.go index 4db945810..2d4cb15a0 100644 --- a/image.go +++ b/image.go @@ -95,15 +95,28 @@ func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) { return } +// DrawLine draws a line. +func (i *Image) DrawLine(x0, y0, x1, y1 int, clr color.Color) error { + return i.DrawLines(&line{x0, y0, x1, y1, clr}) +} + +// DrawLines draws lines. +func (i *Image) DrawLines(lines Lines) (err error) { + ui.Use(func(c *opengl.Context) { + err = i.framebuffer.DrawLines(c, lines) + }) + return +} + // DrawRect draws a rectangle. -func (i *Image) DrawRect(x, y, width, height int, clr color.Color) error { - return i.DrawRects(&rect{x, y, width, height, clr}) +func (i *Image) FillRect(x, y, width, height int, clr color.Color) error { + return i.FillRects(&rect{x, y, width, height, clr}) } // DrawRects draws rectangles on the image. -func (i *Image) DrawRects(rects Rects) (err error) { +func (i *Image) FillRects(rects Rects) (err error) { ui.Use(func(c *opengl.Context) { - err = i.framebuffer.DrawRects(c, rects) + err = i.framebuffer.FillRects(c, rects) }) return } diff --git a/internal/graphics/framebuffer.go b/internal/graphics/framebuffer.go index 9707a53d0..d648f1a95 100644 --- a/internal/graphics/framebuffer.go +++ b/internal/graphics/framebuffer.go @@ -114,16 +114,30 @@ func (f *Framebuffer) DrawTexture(c *opengl.Context, t *Texture, quads TextureQu return shader.DrawTexture(c, t.native, p, quads, geo, clr) } +type Lines interface { + Len() int + Points(i int) (x0, y0, x1, y1 int) + Color(i int) color.Color +} + +func (f *Framebuffer) DrawLines(c *opengl.Context, lines Lines) error { + if err := f.setAsViewport(c); err != nil { + return err + } + p := f.projectionMatrix() + return shader.DrawLines(c, p, lines) +} + type Rects interface { Len() int Rect(i int) (x, y, width, height int) Color(i int) color.Color } -func (f *Framebuffer) DrawRects(c *opengl.Context, rects Rects) error { +func (f *Framebuffer) FillRects(c *opengl.Context, rects Rects) error { if err := f.setAsViewport(c); err != nil { return err } p := f.projectionMatrix() - return shader.DrawRects(c, p, rects) + return shader.FillRects(c, p, rects) } diff --git a/internal/graphics/internal/shader/draw.go b/internal/graphics/internal/shader/draw.go index 7c6bb77f1..b47c2e0ec 100644 --- a/internal/graphics/internal/shader/draw.go +++ b/internal/graphics/internal/shader/draw.go @@ -85,7 +85,51 @@ func DrawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4 return nil } c.BufferSubData(c.ArrayBuffer, vertices) - c.DrawElements(6 * num) + c.DrawElements(c.Triangles, 6*num) + return nil +} + +type Lines interface { + Len() int + Points(i int) (x0, y0, x1, y1 int) + Color(i int) color.Color +} + +func DrawLines(c *opengl.Context, projectionMatrix *[4][4]float64, lines Lines) error { + if !initialized { + if err := initialize(c); err != nil { + return err + } + initialized = true + } + + if lines.Len() == 0 { + return nil + } + + f := useProgramLines(c, glMatrix(projectionMatrix)) + defer f.FinishProgram() + + vertices := vertices[0:0] + num := 0 + for i := 0; i < lines.Len(); i++ { + x0, y0, x1, y1 := lines.Points(i) + if x0 == x1 && y0 == y1 { + continue + } + r, g, b, a := lines.Color(i).RGBA() + vertices = append(vertices, + int16(x0), int16(y0), int16(r), int16(g), int16(b), int16(a), + int16(x1), int16(y1), int16(r), int16(g), int16(b), int16(a), + ) + num++ + } + if len(vertices) == 0 { + return nil + } + c.BufferSubData(c.ArrayBuffer, vertices) + c.DrawElements(c.Lines, 2*num) + return nil } @@ -95,7 +139,7 @@ type Rects interface { Color(i int) color.Color } -func DrawRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects) error { +func FillRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects) error { if !initialized { if err := initialize(c); err != nil { return err @@ -107,7 +151,7 @@ func DrawRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects) return nil } - f := useProgramRect(c, glMatrix(projectionMatrix)) + f := useProgramRects(c, glMatrix(projectionMatrix)) defer f.FinishProgram() vertices := vertices[0:0] @@ -131,7 +175,7 @@ func DrawRects(c *opengl.Context, projectionMatrix *[4][4]float64, rects Rects) return nil } c.BufferSubData(c.ArrayBuffer, vertices) - c.DrawElements(6 * num) + c.DrawElements(c.Triangles, 6*num) return nil } diff --git a/internal/graphics/internal/shader/program.go b/internal/graphics/internal/shader/program.go index ebe22d285..fe7803314 100644 --- a/internal/graphics/internal/shader/program.go +++ b/internal/graphics/internal/shader/program.go @@ -16,13 +16,20 @@ package shader import ( "github.com/hajimehoshi/ebiten/internal/opengl" + "math" +) + +var ( + indexBufferLines opengl.Buffer + indexBufferQuads opengl.Buffer ) var ( programTexture opengl.Program - programRect opengl.Program + programSolid opengl.Program ) +// TODO: Use math.MaxUint16?? const quadsMaxNum = 65536 / 6 // unsafe.SizeOf can't be used because unsafe doesn't work with GopherJS. @@ -47,11 +54,11 @@ func initialize(c *opengl.Context) error { } defer c.DeleteShader(shaderFragmentTextureNative) - shaderFragmentRectNative, err := c.NewShader(c.FragmentShader, shader(c, shaderFragmentRect)) + shaderFragmentSolidNative, err := c.NewShader(c.FragmentShader, shader(c, shaderFragmentSolid)) if err != nil { return err } - defer c.DeleteShader(shaderFragmentRectNative) + defer c.DeleteShader(shaderFragmentSolidNative) programTexture, err = c.NewProgram([]opengl.Shader{ shaderVertexNative, @@ -61,9 +68,9 @@ func initialize(c *opengl.Context) error { return err } - programRect, err = c.NewProgram([]opengl.Shader{ + programSolid, err = c.NewProgram([]opengl.Shader{ shaderVertexColorNative, - shaderFragmentRectNative, + shaderFragmentSolidNative, }) if err != nil { return err @@ -81,7 +88,14 @@ func initialize(c *opengl.Context) error { indices[6*i+4] = 4*i + 2 indices[6*i+5] = 4*i + 3 } - c.NewBuffer(c.ElementArrayBuffer, indices, c.StaticDraw) + indexBufferQuads = c.NewBuffer(c.ElementArrayBuffer, indices, c.StaticDraw) + + // TODO: Use math.MaxUint16 + indices = make([]uint16, 65536) + for i := uint16(0); i < math.MaxUint16; i++ { + indices[i] = i + } + indexBufferLines = c.NewBuffer(c.ElementArrayBuffer, indices, c.StaticDraw) return nil } @@ -101,6 +115,8 @@ func useProgramTexture(c *opengl.Context, projectionMatrix []float32, texture op } program := programTexture + c.BindElementArrayBuffer(indexBufferQuads) + c.UniformFloats(program, "projection_matrix", projectionMatrix) ma := float32(geo.Element(0, 0)) @@ -153,12 +169,38 @@ func useProgramTexture(c *opengl.Context, projectionMatrix []float32, texture op } } -func useProgramRect(c *opengl.Context, projectionMatrix []float32) programFinisher { - if !lastProgram.Equals(programRect) { - c.UseProgram(programRect) - lastProgram = programRect +func useProgramLines(c *opengl.Context, projectionMatrix []float32) programFinisher { + if !lastProgram.Equals(programSolid) { + c.UseProgram(programSolid) + lastProgram = programSolid } - program := programRect + program := programSolid + + c.BindElementArrayBuffer(indexBufferLines) + + c.UniformFloats(program, "projection_matrix", projectionMatrix) + + c.EnableVertexAttribArray(program, "vertex") + c.EnableVertexAttribArray(program, "color") + + // TODO: Change to floats? + c.VertexAttribPointer(program, "vertex", true, false, int16Size*6, 2, uintptr(int16Size*0)) + c.VertexAttribPointer(program, "color", false, true, int16Size*6, 4, uintptr(int16Size*2)) + + return func() { + c.DisableVertexAttribArray(program, "color") + c.DisableVertexAttribArray(program, "vertex") + } +} + +func useProgramRects(c *opengl.Context, projectionMatrix []float32) programFinisher { + if !lastProgram.Equals(programSolid) { + c.UseProgram(programSolid) + lastProgram = programSolid + } + program := programSolid + + c.BindElementArrayBuffer(indexBufferQuads) c.UniformFloats(program, "projection_matrix", projectionMatrix) diff --git a/internal/graphics/internal/shader/shader.go b/internal/graphics/internal/shader/shader.go index 7edaf6d29..4bf2b7337 100644 --- a/internal/graphics/internal/shader/shader.go +++ b/internal/graphics/internal/shader/shader.go @@ -25,7 +25,7 @@ const ( shaderVertex shaderId = iota shaderVertexColor shaderFragmentTexture - shaderFragmentRect + shaderFragmentSolid ) func shader(c *opengl.Context, id shaderId) string { @@ -83,7 +83,7 @@ void main(void) { gl_FragColor = color; } `, - shaderFragmentRect: ` + shaderFragmentSolid: ` varying lowp vec4 vertex_out_color; void main(void) { diff --git a/internal/graphics/texture.go b/internal/graphics/texture.go index d3fc6895d..31e3e48a6 100644 --- a/internal/graphics/texture.go +++ b/internal/graphics/texture.go @@ -54,7 +54,7 @@ func (t *Texture) Size() (width, height int) { return t.width, t.height } -func NewTexture(c *opengl.Context, width, height int, filter opengl.FilterType) (*Texture, error) { +func NewTexture(c *opengl.Context, width, height int, filter opengl.Filter) (*Texture, error) { w := internal.NextPowerOf2Int(width) h := internal.NextPowerOf2Int(height) if w < 4 { @@ -70,7 +70,7 @@ func NewTexture(c *opengl.Context, width, height int, filter opengl.FilterType) return &Texture{native, width, height}, nil } -func NewTextureFromImage(c *opengl.Context, img image.Image, filter opengl.FilterType) (*Texture, error) { +func NewTextureFromImage(c *opengl.Context, img image.Image, filter opengl.Filter) (*Texture, error) { origSize := img.Bounds().Size() if origSize.X < 4 { return nil, errors.New("width must be equal or more than 4.") diff --git a/internal/opengl/context.go b/internal/opengl/context.go index 3216481ef..7d475ff3a 100644 --- a/internal/opengl/context.go +++ b/internal/opengl/context.go @@ -22,10 +22,13 @@ import ( "github.com/go-gl/gl" ) +// TODO: Why int? + type Texture int type Framebuffer int type Shader int type Program int +type Buffer gl.Buffer // TODO: Remove this after the GopherJS bug was fixed (#159) func (p Program) Equals(other Program) bool { @@ -53,6 +56,8 @@ func NewContext() *Context { ElementArrayBuffer: gl.ELEMENT_ARRAY_BUFFER, DynamicDraw: gl.DYNAMIC_DRAW, StaticDraw: gl.STATIC_DRAW, + Triangles: gl.TRIANGLES, + Lines: gl.LINES, } c.init() return c @@ -65,7 +70,7 @@ func (c *Context) init() { gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) } -func (c *Context) NewTexture(width, height int, pixels []uint8, filter FilterType) (Texture, error) { +func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) { t := gl.GenTexture() if t < 0 { return 0, errors.New("glGenTexture failed") @@ -231,8 +236,9 @@ func (c *Context) DisableVertexAttribArray(p Program, location string) { l.DisableArray() } -func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsageType BufferUsageType) { - gl.GenBuffer().Bind(gl.GLenum(bufferType)) +func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer { + b := gl.GenBuffer() + b.Bind(gl.GLenum(bufferType)) size := 0 ptr := v switch v := v.(type) { @@ -246,7 +252,12 @@ func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsageTyp default: panic("not reach") } - gl.BufferData(gl.GLenum(bufferType), size, ptr, gl.GLenum(bufferUsageType)) + gl.BufferData(gl.GLenum(bufferType), size, ptr, gl.GLenum(bufferUsage)) + return Buffer(b) +} + +func (c *Context) BindElementArrayBuffer(b Buffer) { + gl.Buffer(b).Bind(gl.ELEMENT_ARRAY_BUFFER) } func (c *Context) BufferSubData(bufferType BufferType, data []int16) { @@ -254,8 +265,8 @@ func (c *Context) BufferSubData(bufferType BufferType, data []int16) { gl.BufferSubData(gl.GLenum(bufferType), 0, int16Size*len(data), data) } -func (c *Context) DrawElements(len int) { - gl.DrawElements(gl.TRIANGLES, len, gl.UNSIGNED_SHORT, uintptr(0)) +func (c *Context) DrawElements(mode Mode, len int) { + gl.DrawElements(gl.GLenum(mode), len, gl.UNSIGNED_SHORT, uintptr(0)) } func (c *Context) Flush() { diff --git a/internal/opengl/context_js.go b/internal/opengl/context_js.go index 4bc880a0a..581da7d1e 100644 --- a/internal/opengl/context_js.go +++ b/internal/opengl/context_js.go @@ -39,6 +39,10 @@ type Program struct { js.Object } +type Buffer struct { + js.Object +} + // TODO: Remove this after the GopherJS bug was fixed (#159) func (p Program) Equals(other Program) bool { return p.Object == other.Object @@ -62,14 +66,16 @@ type context struct { func NewContext(gl *webgl.Context) *Context { c := &Context{ - Nearest: FilterType(gl.NEAREST), - Linear: FilterType(gl.LINEAR), + Nearest: Filter(gl.NEAREST), + Linear: Filter(gl.LINEAR), VertexShader: ShaderType(gl.VERTEX_SHADER), FragmentShader: ShaderType(gl.FRAGMENT_SHADER), ArrayBuffer: BufferType(gl.ARRAY_BUFFER), ElementArrayBuffer: BufferType(gl.ELEMENT_ARRAY_BUFFER), - DynamicDraw: BufferUsageType(gl.DYNAMIC_DRAW), - StaticDraw: BufferUsageType(gl.STATIC_DRAW), + DynamicDraw: BufferUsage(gl.DYNAMIC_DRAW), + StaticDraw: BufferUsage(gl.STATIC_DRAW), + Triangles: Mode(gl.TRIANGLES), + Lines: Mode(gl.LINES), } c.gl = gl c.init() @@ -83,7 +89,7 @@ func (c *Context) init() { gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) } -func (c *Context) NewTexture(width, height int, pixels []uint8, filter FilterType) (Texture, error) { +func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) { gl := c.gl t := gl.CreateTexture() if t == nil { @@ -284,11 +290,17 @@ func (c *Context) DisableVertexAttribArray(p Program, location string) { gl.DisableVertexAttribArray(int(l)) } -func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsageType BufferUsageType) { +func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer { gl := c.gl b := gl.CreateBuffer() gl.BindBuffer(int(bufferType), b) - gl.BufferData(int(bufferType), v, int(bufferUsageType)) + gl.BufferData(int(bufferType), v, int(bufferUsage)) + return Buffer{b} +} + +func (c *Context) BindElementArrayBuffer(b Buffer) { + gl := c.gl + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.Object) } func (c *Context) BufferSubData(bufferType BufferType, data []int16) { @@ -296,9 +308,9 @@ func (c *Context) BufferSubData(bufferType BufferType, data []int16) { gl.BufferSubData(int(bufferType), 0, data) } -func (c *Context) DrawElements(len int) { +func (c *Context) DrawElements(mode Mode, len int) { gl := c.gl - gl.DrawElements(gl.TRIANGLES, len, gl.UNSIGNED_SHORT, 0) + gl.DrawElements(int(mode), len, gl.UNSIGNED_SHORT, 0) } func (c *Context) Flush() { diff --git a/internal/opengl/types.go b/internal/opengl/types.go index 933ce09f9..e45fed225 100644 --- a/internal/opengl/types.go +++ b/internal/opengl/types.go @@ -14,19 +14,22 @@ package opengl -type FilterType int +type Filter int type ShaderType int type BufferType int -type BufferUsageType int +type BufferUsage int +type Mode int type Context struct { - Nearest FilterType - Linear FilterType + Nearest Filter + Linear Filter VertexShader ShaderType FragmentShader ShaderType ArrayBuffer BufferType ElementArrayBuffer BufferType - DynamicDraw BufferUsageType - StaticDraw BufferUsageType + DynamicDraw BufferUsage + StaticDraw BufferUsage + Triangles Mode + Lines Mode context } diff --git a/shapes.go b/shapes.go index 1b98eef27..2d5392644 100644 --- a/shapes.go +++ b/shapes.go @@ -1,4 +1,4 @@ -// Copyright 2014 Hajime Hoshi +// Copyright 2015 Hajime Hoshi // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,10 +18,35 @@ import ( "image/color" ) +// A Lines represents the set of lines. +type Lines interface { + Len() int + Points(i int) (x0, y0, x1, y1 int) // TODO: Change to float64? + Color(i int) color.Color +} + +type line struct { + x0, y0 int + x1, y1 int + color color.Color +} + +func (l *line) Len() int { + return 1 +} + +func (l *line) Points(i int) (x0, y0, x1, y1 int) { + return l.x0, l.y0, l.x1, l.y1 +} + +func (l *line) Color(i int) color.Color { + return l.color +} + // A Rects represents the set of rectangles. type Rects interface { Len() int - Rect(i int) (x, y, width, height int) + Rect(i int) (x, y, width, height int) // TODO: Change to float64? Color(i int) color.Color }