Skip to content

Instantly share code, notes, and snippets.

@firelizzard18
Created June 6, 2021 21:08
Show Gist options
  • Save firelizzard18/1c891f48ddd59a1a5dd0d8153fbaa7e9 to your computer and use it in GitHub Desktop.
Save firelizzard18/1c891f48ddd59a1a5dd0d8153fbaa7e9 to your computer and use it in GitHub Desktop.
GTK go-gl/examples/gl41core-cube
// Copyright 2014 The go-gl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Renders a textured spinning cube using GLFW 3 and OpenGL 4.1 core forward-compatible profile.
package main
import (
"fmt"
"image"
"image/draw"
_ "image/png"
"log"
"os"
"runtime"
"strings"
"unsafe"
"github.com/go-gl/gl/v4.6-core/gl"
"github.com/go-gl/mathgl/mgl32"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
)
const windowWidth = 800
const windowHeight = 600
func init() {
// GLFW event handling must run on the main OS thread
runtime.LockOSThread()
}
func main() {
gtk.Init(nil)
win, _ := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
win.SetTitle("Cube")
win.SetDefaultSize(800, 600)
win.Connect("destroy", func() {
gtk.MainQuit()
})
gla, _ := gtk.GLAreaNew()
gla.Connect("realize", realize)
gla.Connect("unrealize", unrealize)
gla.Connect("render", render)
win.Add(gla)
win.ShowAll()
gtk.Main()
}
func newProgram(vertexShaderSource, 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", log)
}
return shader, nil
}
func newTexture(file string) (uint32, error) {
imgFile, err := os.Open(file)
if err != nil {
return 0, fmt.Errorf("texture %q not found on disk: %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
}
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"
var cubeVertices = []float32{
// X, Y, Z, 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 (
program, texture, vao, vbo uint32
modelUniform int32
angle float32
previousTime int64
model mgl32.Mat4
)
func realize(gla *gtk.GLArea) {
gla.MakeCurrent()
// Initialize Glow
err := gl.Init()
if err != nil {
log.Fatal(err)
}
gl.Enable(gl.DEBUG_OUTPUT)
gl.Enable(gl.DEBUG_OUTPUT_SYNCHRONOUS)
gl.DebugMessageCallback(debug, nil)
version := gl.GoStr(gl.GetString(gl.VERSION))
fmt.Println("OpenGL version", version)
// Configure the vertex and fragment shaders
program, err = newProgram(vertexShader, fragmentShader)
if err != nil {
log.Fatal(err)
}
gl.UseProgram(program)
projection := mgl32.Perspective(mgl32.DegToRad(45.0), float32(windowWidth)/windowHeight, 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"))
// Load the texture
texture, err = newTexture("square.png")
if err != nil {
log.Fatalln(err)
}
// Configure the vertex data
gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao)
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)
previousTime = gla.GetFrameClock().GetFrameTime()
gla.AddTickCallback(func(widget *gtk.Widget, frameClock *gdk.FrameClock) bool {
widget.QueueDraw()
return true
})
}
func unrealize(gla *gtk.GLArea) {
}
func render(gla *gtk.GLArea) {
gla.MakeCurrent()
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// Update
time := gla.GetFrameClock().GetFrameTime()
elapsed := float32(time-previousTime) / 1000000
previousTime = time
angle += elapsed
model = mgl32.HomogRotate3D(float32(angle), mgl32.Vec3{0, 1, 0})
// Render
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)
}
func debug(source, gltype, id, severity uint32, length int32, message string, userParam unsafe.Pointer) {
log.Printf("[DEBUG] %s {src=%v type=%v sev=%v}", message, source, gltype, severity)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment