package main

import (
"encoding/binary"
"log"

"golang.org/x/mobile/app"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/exp/app/debug"
"golang.org/x/mobile/exp/f32"
"golang.org/x/mobile/exp/gl/glutil"
"golang.org/x/mobile/gl"
)

var (
images   *glutil.Images
fps      *debug.FPS
program  gl.Program
position gl.Attrib
offset   gl.Uniform
color    gl.Uniform
buf      gl.Buffer

green  float32
touchX float32
touchY float32
)

func main() {
app.Main(func(a app.App) {
var glctx gl.Context
var sz size.Event
for e := range a.Events() {
switch e := a.Filter(e).(type) {
case lifecycle.Event:
switch e.Crosses(lifecycle.StageVisible) {
case lifecycle.CrossOn:
glctx, _ = e.DrawContext.(gl.Context)
onStart(glctx)
a.Send(paint.Event{})
case lifecycle.CrossOff:
onStop(glctx)
glctx = nil
}
case size.Event:
sz = e
touchX = float32(sz.WidthPx / 2)
touchY = float32(sz.HeightPx / 2)
case paint.Event:
if glctx == nil || e.External {
// As we are actively painting as fast as
// we can (usually 60 FPS), skip any paint
// events sent by the system.
continue
}

onPaint(glctx, sz)
a.Publish()
// Drive the animation by preparing to paint the next frame
// after this one is shown.
a.Send(paint.Event{})
case touch.Event:
touchX = e.X
touchY = e.Y
}
}
})
}

func onStart(glctx gl.Context) {
var err error
program, err = glutil.CreateProgram(glctx, vertexShader, fragmentShader)
if err != nil {
log.Printf("error creating GL program: %v", err)
return
}

buf = glctx.CreateBuffer()
glctx.BindBuffer(gl.ARRAY_BUFFER, buf)
glctx.BufferData(gl.ARRAY_BUFFER, triangleData, gl.STATIC_DRAW)

position = glctx.GetAttribLocation(program, "position")
color = glctx.GetUniformLocation(program, "color")
offset = glctx.GetUniformLocation(program, "offset")

images = glutil.NewImages(glctx)
fps = debug.NewFPS(images)
}

func onStop(glctx gl.Context) {
glctx.DeleteProgram(program)
glctx.DeleteBuffer(buf)
fps.Release()
images.Release()
}

func onPaint(glctx gl.Context, sz size.Event) {
glctx.ClearColor(1, 0, 0, 1)
glctx.Clear(gl.COLOR_BUFFER_BIT)

glctx.UseProgram(program)

green += 0.01
if green > 1 {
green = 0
}
glctx.Uniform4f(color, 0, green, 0, 1)

glctx.Uniform2f(offset, touchX/float32(sz.WidthPx), touchY/float32(sz.HeightPx))

glctx.BindBuffer(gl.ARRAY_BUFFER, buf)
glctx.EnableVertexAttribArray(position)
glctx.VertexAttribPointer(position, coordsPerVertex, gl.FLOAT, false, 0, 0)
glctx.DrawArrays(gl.TRIANGLES, 0, vertexCount)
glctx.DisableVertexAttribArray(position)

fps.Draw(sz)
}

var triangleData = f32.Bytes(binary.LittleEndian,
0.0, 0.4, 0.0, // top left
0.0, 0.0, 0.0, // bottom left
0.4, 0.0, 0.0, // bottom right
)

const (
coordsPerVertex = 3
vertexCount     = 3
)

const vertexShader = `#version 100
uniform vec2 offset;

attribute vec4 position;
void main() {
// offset comes in with x/y values between 0 and 1.
// position bounds are -1 to 1.
vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);
gl_Position = position + offset4;
}`

const fragmentShader = `#version 100
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}`

总结:
1、用gomobile做aar类库给安卓用,还是挺方便的
2、go类库可以跨平台,在mac 编译就可以用于iOS的app
3、go类库由于是机器编译,直接产生机器码,所以对部分模块的保密或代码保护,是很好的方案
4、all-go的app,适用于Android和iOS,特别适合性能至上的游戏app,但用于做业务类app生产力太低
5、go使用起来是很简单的,你值得尝试,特别是服务端协程的高并发支持,非常棒,这样使得go在前端和server端,都可以有用武之地