| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build darwin || linux || windows |
| // +build darwin linux windows |
| |
| // An app that draws a green triangle on a red background. |
| // |
| // In order to build this program as an Android APK, using the gomobile tool. |
| // |
| // See http://godoc.org/golang.org/x/mobile/cmd/gomobile to install gomobile. |
| // |
| // Get the basic example and use gomobile to build or install it on your device. |
| // |
| // $ go get -d golang.org/x/mobile/example/basic |
| // $ gomobile build golang.org/x/mobile/example/basic # will build an APK |
| // |
| // # plug your Android device to your computer or start an Android emulator. |
| // # if you have adb installed on your machine, use gomobile install to |
| // # build and deploy the APK to an Android target. |
| // $ gomobile install golang.org/x/mobile/example/basic |
| // |
| // Switch to your device or emulator to start the Basic application from |
| // the launcher. |
| // You can also run the application on your desktop by running the command |
| // below. (Note: It currently doesn't work on Windows.) |
| // |
| // $ go install golang.org/x/mobile/example/basic && basic |
| 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; |
| }` |