Last active
July 26, 2017 10:05
-
-
Save Grief/93dffffe0aaeba1af75c621c7a20b801 to your computer and use it in GitHub Desktop.
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
package main | |
import ( | |
"github.com/go-gl/gl/v4.5-core/gl" | |
"github.com/go-gl/glfw/v3.2/glfw" | |
"github.com/go-gl/mathgl/mgl32" | |
"golang.org/x/image/draw" | |
"image" | |
_ "image/png" | |
"io" | |
"os" | |
p "path" | |
"runtime" | |
) | |
func main() { | |
StartEngine(func() { | |
//texture = LoadTexture("square.png") // TEXTURE LOADS HERE WITHOUT AN ISSUE | |
CreateWindow(WindowCfg{ | |
Init: func() { | |
var vao uint32 | |
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) | |
}, | |
}, func() { | |
renderCube() | |
}) | |
texture = LoadTexture("square.png") // TEXTURE LOADED HERE CAUSES BLACK BORDER | |
program = NewProgram(vertexShader, fragmentShader) | |
transform = mgl32.Perspective(mgl32.DegToRad(45.0), float32(800)/600, 0.1, 10.0) | |
transform = transform.Mul4(mgl32.LookAtV(mgl32.Vec3{3, 3, 3}, mgl32.Vec3{0, 0, 0}, mgl32.Vec3{0, 1, 0})) | |
transform = transform.Mul4(mgl32.HomogRotate3D(float32(2.0), mgl32.Vec3{0, 1, 0})) | |
transformUniform = gl.GetUniformLocation(program, gl.Str("transform\x00")) | |
textureUniform = gl.GetUniformLocation(program, gl.Str("tex\x00")) | |
vertAttrib = uint32(gl.GetAttribLocation(program, gl.Str("vert\x00"))) | |
texCoordAttrib = uint32(gl.GetAttribLocation(program, gl.Str("vertTexCoord\x00"))) | |
}) | |
} | |
type task struct { | |
fn func() | |
notify chan bool | |
} | |
type Pipe chan task | |
type Window struct { | |
native *glfw.Window | |
threadContext Pipe | |
renderHandler func() | |
} | |
type WindowCfg struct { | |
Init func() | |
} | |
type file struct { | |
path string | |
} | |
type ReadStream io.Reader | |
var rootDirectory string | |
var vbo uint32 | |
var transformUniform int32 | |
var textureUniform int32 | |
var texture uint32 | |
var vertAttrib uint32 | |
var texCoordAttrib uint32 | |
var program uint32 | |
var transform = mgl32.Ident4() | |
var shareContext *glfw.Window | |
var mainThreadContext = CreatePipe() | |
func renderCube() { | |
gl.ClearColor(0, 0.1, 0, 0) | |
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
if vbo == 0 { | |
return | |
} | |
gl.BindBuffer(gl.ARRAY_BUFFER, vbo) | |
gl.UseProgram(program) | |
gl.Enable(gl.DEPTH_TEST) | |
gl.DepthFunc(gl.LESS) | |
gl.Uniform1i(textureUniform, 0) | |
gl.EnableVertexAttribArray(vertAttrib) | |
gl.EnableVertexAttribArray(texCoordAttrib) | |
gl.VertexAttribPointer(vertAttrib, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0)) | |
gl.VertexAttribPointer(texCoordAttrib, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4)) | |
gl.UniformMatrix4fv(transformUniform, 1, false, &transform[0]) | |
gl.ActiveTexture(gl.TEXTURE0) | |
gl.BindTexture(gl.TEXTURE_2D, texture) | |
gl.DrawArrays(gl.TRIANGLES, 0, 6*2*3) | |
} | |
func runInMainThread(fn func()) { | |
mainThreadContext.Run(fn, glfw.PostEmptyEvent) | |
} | |
func StartEngine(fnLoad func()) { | |
runtime.LockOSThread() | |
var err error | |
if err = glfw.Init(); err != nil { | |
panic(err) | |
} | |
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) | |
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) | |
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) | |
glfw.WindowHint(glfw.ContextVersionMajor, 4) | |
glfw.WindowHint(glfw.ContextVersionMinor, 5) | |
glfw.WindowHint(glfw.Visible, glfw.False) | |
shareContext, err = glfw.CreateWindow(100, 100, "", nil, nil) | |
glfw.WindowHint(glfw.Visible, glfw.True) | |
shareContext.MakeContextCurrent() | |
gl.Init() | |
go func() { | |
runInMainThread(func() { | |
fnLoad() | |
}) | |
}() | |
for { | |
mainThreadContext.ExecuteReceivedTasks() | |
glfw.WaitEvents() | |
} | |
glfw.Terminate() | |
} | |
func NewProgram(vertexShaderSource, fragmentShaderSource string) uint32 { | |
vertexShader, fragmentShader := compileShader(vertexShaderSource, gl.VERTEX_SHADER), compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER) | |
program := gl.CreateProgram() | |
gl.AttachShader(program, vertexShader) | |
gl.AttachShader(program, fragmentShader) | |
gl.LinkProgram(program) | |
return program | |
} | |
func CreateWindow(cfg WindowCfg, renderHandler func()) Window { | |
var window Window | |
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) | |
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) | |
glfw.WindowHint(glfw.ContextVersionMajor, 4) | |
glfw.WindowHint(glfw.ContextVersionMinor, 5) | |
//shareContext.MakeContextCurrent() | |
nativeWindow, err := glfw.CreateWindow(800, 600, "", nil, shareContext) | |
if err != nil { | |
panic(err) | |
} | |
window = Window{ | |
native: nativeWindow, | |
renderHandler: renderHandler, | |
threadContext: CreatePipe(), | |
} | |
go func() { | |
runtime.LockOSThread() | |
window.native.MakeContextCurrent() | |
glfw.SwapInterval(1) | |
cfg.Init() | |
for !window.native.ShouldClose() { | |
window.threadContext.ExecuteReceivedTasks() | |
window.renderHandler() | |
window.native.SwapBuffers() | |
} | |
}() | |
return window | |
} | |
func compileShader(source string, shaderType uint32) uint32 { | |
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) | |
return shader | |
} | |
func LoadTexture(path ...string) uint32 { | |
var id uint32 | |
File(path...).Read(func(stream ReadStream) { | |
img, _, _ := image.Decode(stream) | |
rgba := image.NewRGBA(img.Bounds()) | |
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src) | |
gl.GenTextures(1, &id) | |
gl.ActiveTexture(gl.TEXTURE0) | |
gl.BindTexture(gl.TEXTURE_2D, id) | |
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 id | |
} | |
func CreatePipe() Pipe { | |
return make(Pipe, 1) | |
} | |
func (p Pipe) Run(fn func(), notify func()) { | |
resume := make(chan bool) | |
task := task{fn: fn, notify: resume} | |
p <- task | |
if notify != nil { | |
notify() | |
} | |
_ = <-resume | |
} | |
func (p Pipe) ExecuteReceivedTasks() { | |
select { | |
case task := <-p: | |
task.fn() | |
close(task.notify) | |
default: | |
} | |
} | |
func init() { | |
ex, err := os.Executable() | |
if err != nil { | |
panic(err) | |
} | |
rootDirectory = p.Dir(ex) | |
} | |
func File(path ...string) file { | |
return file{path: p.Join(append([]string{rootDirectory}, path...)...)} | |
} | |
func (f file) Read(fn func(stream ReadStream)) { | |
file, err := os.Open(f.path) | |
if err != nil { | |
panic(err) | |
} | |
fn(file) | |
defer file.Close() | |
} | |
var vertexShader = ` | |
#version 330 | |
uniform mat4 transform; | |
in vec3 vert; | |
in vec2 vertTexCoord; | |
out vec2 fragTexCoord; | |
void main() { | |
fragTexCoord = vertTexCoord; | |
gl_Position = transform * 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, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment