Created
June 6, 2021 21:08
-
-
Save firelizzard18/1c891f48ddd59a1a5dd0d8153fbaa7e9 to your computer and use it in GitHub Desktop.
GTK go-gl/examples/gl41core-cube
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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