Following a couple different tutorials, got a rotating cube

main
Sean Hickey 2021-08-24 22:44:18 -07:00
commit 21f16bf5c3
6 changed files with 433 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*~
carpy-breakout
output.log

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
all: dependencies build
dependencies:
go mod tidy
build:
go build

113
glhelpers/glhelpers.go Normal file
View File

@ -0,0 +1,113 @@
package glhelpers
// https://github.com/go-gl/example
import (
"os"
"image"
"image/draw"
_ "image/png"
"fmt"
"strings"
gl "github.com/go-gl/gl/v3.1/gles2"
)
func NewProgram(vertexShaderSource string, fragmentShaderSource string) (uint32, error) {
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
if err != nil {
return 0, err
}
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
if err != nil {
return 0, err
}
program := gl.CreateProgram()
gl.AttachShader(program, vertexShader)
gl.AttachShader(program, fragmentShader)
gl.LinkProgram(program)
var status int32
gl.GetProgramiv(program, gl.LINK_STATUS, &status)
if status == gl.FALSE {
var logLength int32
gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log))
return 0, fmt.Errorf("failed to link program: %v", log)
}
gl.DeleteShader(vertexShader)
gl.DeleteShader(fragmentShader)
return program, nil
}
func compileShader(source string, shaderType uint32) (uint32, error) {
shader := gl.CreateShader(shaderType)
csources, free := gl.Strs(source)
gl.ShaderSource(shader, 1, csources, nil)
free()
gl.CompileShader(shader)
var status int32
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
if status == gl.FALSE {
var logLength int32
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
log := strings.Repeat("\x00", int(logLength + 1))
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
return 0, fmt.Errorf("failed to compile %v: %v", source, log)
}
return shader, nil
}
func NewTexture(file string) (uint32, error) {
imgFile, err := os.Open(file)
if err != nil {
return 0, fmt.Errorf("texture %q file not found found: %v", file, err)
}
img, _, err := image.Decode(imgFile)
if err != nil {
return 0, err
}
rgba := image.NewRGBA(img.Bounds())
if rgba.Stride != rgba.Rect.Size().X*4 {
return 0, fmt.Errorf("unsupported stride")
}
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
var texture uint32
gl.GenTextures(1, &texture)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, texture)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.TexImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
int32(rgba.Rect.Size().X),
int32(rgba.Rect.Size().Y),
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(rgba.Pix),
)
return texture, nil
}

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module gitea.wisellama.rocks/carpy-breakout
go 1.17
require (
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f
github.com/go-gl/mathgl v1.0.0
github.com/veandco/go-sdl2 v0.4.9
)
require golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f // indirect

9
go.sum Normal file
View File

@ -0,0 +1,9 @@
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU=
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
github.com/go-gl/mathgl v1.0.0 h1:t9DznWJlXxxjeeKLIdovCOVJQk/GzDEL7h/h+Ro2B68=
github.com/go-gl/mathgl v1.0.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
github.com/veandco/go-sdl2 v0.4.9 h1:Fc0/Xj8JToXO7qia77Vc1M6bP7iufE+vzsLNah70Y6M=
github.com/veandco/go-sdl2 v0.4.9/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f h1:FO4MZ3N56GnxbqxGKqh+YTzUWQ2sDwtFQEZgLOxh9Jc=
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

289
main.go Normal file
View File

@ -0,0 +1,289 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"runtime"
"runtime/pprof"
"github.com/veandco/go-sdl2/sdl"
gl "github.com/go-gl/gl/v3.1/gles2"
"github.com/go-gl/mathgl/mgl32"
"gitea.wisellama.rocks/carpy-breakout/glhelpers"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
const GameTitle = "Carpy Breakout"
const DefaultWindowWidth int32 = 800
const DefaultWindowHeight int32 = 600
const DefaultWindowFlags uint32 = sdl.WINDOW_SHOWN | sdl.WINDOW_RESIZABLE | sdl.WINDOW_OPENGL
const FullscreenWindowFlags uint32 = DefaultWindowFlags | sdl.WINDOW_FULLSCREEN_DESKTOP
const LogFile = "output.log"
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
setupLogging()
run()
}
func run() {
runtime.LockOSThread() // TODO can rework stuff to not need this.
log.Println(os.Environ())
err := sdl.Init(sdl.INIT_EVERYTHING)
if err != nil {
panic(err)
}
defer sdl.Quit()
// Disable the Linux compositor flicker.
// https://github.com/mosra/magnum/issues/184#issuecomment-425952900
sdl.SetHint(sdl.HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")
sdl.DisableScreenSaver()
//sdl.GLSetAttribute(sdl.GL_MULTISAMPLESAMPLES, 4) // Smooth
window, err := sdl.CreateWindow(
GameTitle,
sdl.WINDOWPOS_UNDEFINED,
sdl.WINDOWPOS_UNDEFINED,
DefaultWindowWidth,
DefaultWindowHeight,
DefaultWindowFlags)
if err != nil {
panic(err)
}
defer window.Destroy()
context, err := window.GLCreateContext()
if err != nil {
panic(err)
}
defer sdl.GLDeleteContext(context)
width, height := window.GetSize()
gl.Init()
version := gl.GoStr(gl.GetString(gl.VERSION))
log.Println("OpenGL version", version)
gl.Viewport(0, 0, width, height)
gl.ClearColor(0.0, 0.0, 0.0, 1.0)
program, err := glhelpers.NewProgram(vertexShader, fragmentShader)
if err != nil {
panic(err)
}
gl.UseProgram(program)
projection := mgl32.Perspective(mgl32.DegToRad(45.0), float32(width)/float32(height), 0.1, 10.0)
projectionUniform := gl.GetUniformLocation(program, gl.Str("projection\x00"))
gl.UniformMatrix4fv(projectionUniform, 1, false, &projection[0])
camera := mgl32.LookAtV(mgl32.Vec3{3, 3, 3}, mgl32.Vec3{0, 0, 0}, mgl32.Vec3{0, 1, 0})
cameraUniform := gl.GetUniformLocation(program, gl.Str("camera\x00"))
gl.UniformMatrix4fv(cameraUniform, 1, false, &camera[0])
model := mgl32.Ident4()
modelUniform := gl.GetUniformLocation(program, gl.Str("model\x00"))
gl.UniformMatrix4fv(modelUniform, 1, false, &model[0])
textureUniform := gl.GetUniformLocation(program, gl.Str("tex\x00"))
gl.Uniform1i(textureUniform, 0)
//gl.BindFragDataLocation(program, 0, gl.Str("outputColor\x00"))
texture, err := glhelpers.NewTexture("square.png")
if err != nil {
log.Fatal(err)
}
var vao uint32
gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao)
var vbo uint32
gl.GenBuffers(1, &vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(cubeVertices)*4, gl.Ptr(cubeVertices), gl.STATIC_DRAW)
vertAttrib := uint32(gl.GetAttribLocation(program, gl.Str("vert\x00")))
gl.EnableVertexAttribArray(vertAttrib)
gl.VertexAttribPointerWithOffset(vertAttrib, 3, gl.FLOAT, false, 5*4, 0)
texCoordAttrib := uint32(gl.GetAttribLocation(program, gl.Str("vertTexCoord\x00")))
gl.EnableVertexAttribArray(texCoordAttrib)
gl.VertexAttribPointerWithOffset(texCoordAttrib, 2, gl.FLOAT, false, 5*4, 3*4)
// Configure global settings
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
gl.ClearColor(1.0, 1.0, 1.0, 1.0)
running := true
angle := 0.0
for running {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
angle += 0.01
model = mgl32.HomogRotate3D(float32(angle), mgl32.Vec3{0, 1, 0})
gl.UseProgram(program)
gl.UniformMatrix4fv(modelUniform, 1, false, &model[0])
gl.BindVertexArray(vao)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, texture)
gl.DrawArrays(gl.TRIANGLES, 0, 6 * 2 * 3) // 6 sides, 2 triangles, 3 points each
window.GLSwap()
running = handleEvents(running, window)
sdl.Delay(16)
}
}
func setupLogging() {
file, err := os.OpenFile(LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
log.SetOutput(file)
log.Println("=== START ===")
}
func drawSDLRenderer(renderer *sdl.Renderer) {
renderer.SetDrawColor(0, 0, 0, 255)
renderer.Clear()
rect := sdl.Rect{0, 0, 100, 100}
renderer.SetDrawColor(0, 0, 255, 255)
renderer.DrawRect(&rect)
renderer.Present()
}
func handleEvents(running bool, window *sdl.Window) bool {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch t := event.(type) {
case *sdl.QuitEvent:
fmt.Printf("Quit")
running = false
case *sdl.MouseMotionEvent:
fmt.Printf("[%d ms] MouseMotion\ttype:%d\tid:%d\tx:%d\ty:%d\txrel:%d\tyrel:%d\n",
t.Timestamp, t.Type, t.Which, t.X, t.Y, t.XRel, t.YRel)
case *sdl.MouseButtonEvent:
fmt.Printf("[%d ms] MouseButton\ttype:%d\tid:%d\tx:%d\ty:%d\tbutton:%d\tstate:%d\n",
t.Timestamp, t.Type, t.Which, t.X, t.Y, t.Button, t.State)
case *sdl.MouseWheelEvent:
fmt.Printf("[%d ms] MouseWheel\ttype:%d\tid:%d\tx:%d\ty:%d\n",
t.Timestamp, t.Type, t.Which, t.X, t.Y)
case *sdl.KeyboardEvent:
if t.Keysym.Sym == sdl.K_ESCAPE {
fmt.Print("Esc quitting\n")
running = false
}
if t.Keysym.Sym == sdl.K_f {
window.SetFullscreen(FullscreenWindowFlags)
}
if t.Keysym.Sym == sdl.K_g {
window.SetFullscreen(DefaultWindowFlags)
}
}
}
return running
}
var cubeVertices = []float32{
// X, Y, Z, texture U, V
// Bottom
-1.0, -1.0, -1.0, 0.0, 0.0,
1.0, -1.0, -1.0, 1.0, 0.0,
-1.0, -1.0, 1.0, 0.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.0,
1.0, -1.0, 1.0, 1.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 1.0,
// Top
-1.0, 1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, 1.0, 0.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0,
1.0, 1.0, -1.0, 1.0, 0.0,
-1.0, 1.0, 1.0, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0,
// Front
-1.0, -1.0, 1.0, 1.0, 0.0,
1.0, -1.0, 1.0, 0.0, 0.0,
-1.0, 1.0, 1.0, 1.0, 1.0,
1.0, -1.0, 1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 1.0, 1.0,
// Back
-1.0, -1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, -1.0, 0.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.0,
1.0, -1.0, -1.0, 1.0, 0.0,
-1.0, 1.0, -1.0, 0.0, 1.0,
1.0, 1.0, -1.0, 1.0, 1.0,
// Left
-1.0, -1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0,
-1.0, -1.0, -1.0, 0.0, 0.0,
-1.0, -1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 1.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0,
// Right
1.0, -1.0, 1.0, 1.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.0,
1.0, 1.0, -1.0, 0.0, 0.0,
1.0, -1.0, 1.0, 1.0, 1.0,
1.0, 1.0, -1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 0.0, 1.0,
}
var vertexShader = `
#version 330
uniform mat4 projection;
uniform mat4 camera;
uniform mat4 model;
in vec3 vert;
in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main() {
fragTexCoord = vertTexCoord;
gl_Position = projection * camera * model * vec4(vert, 1);
}
` + "\x00"
var fragmentShader = `
#version 330
uniform sampler2D tex;
in vec2 fragTexCoord;
out vec4 outputColor;
void main() {
outputColor = texture(tex, fragTexCoord);
}
` + "\x00"