173 lines
4.8 KiB
Go
173 lines
4.8 KiB
Go
package glshader
|
|
|
|
// https://github.com/go-gl/example
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"image/draw"
|
|
"os"
|
|
"strings"
|
|
|
|
gl "github.com/go-gl/gl/v3.1/gles2"
|
|
"github.com/go-gl/mathgl/mgl32"
|
|
)
|
|
|
|
func NewDefaultProgram() (uint32, error) {
|
|
return NewProgram(VertexShaderSource, FragmentShaderSource)
|
|
}
|
|
|
|
func NewProgram(vertexShaderSource string, fragmentShaderSource string) (uint32, error) {
|
|
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer gl.DeleteShader(vertexShader)
|
|
|
|
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer gl.DeleteShader(fragmentShader)
|
|
|
|
program := gl.CreateProgram()
|
|
|
|
gl.AttachShader(program, vertexShader)
|
|
defer gl.DetachShader(program, vertexShader)
|
|
|
|
gl.AttachShader(program, fragmentShader)
|
|
defer gl.DetachShader(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)
|
|
}
|
|
|
|
return program, nil
|
|
}
|
|
|
|
func compileShader(source string, shaderType uint32) (uint32, error) {
|
|
shader := gl.CreateShader(shaderType)
|
|
if shader == 0 {
|
|
return 0, fmt.Errorf("Error creating shader - type: %v", shaderType)
|
|
}
|
|
|
|
sourceWithZero := source + "\x00"
|
|
csources, free := gl.Strs(sourceWithZero)
|
|
defer free()
|
|
|
|
gl.ShaderSource(shader, 1, csources, nil)
|
|
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: %w", 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
|
|
}
|
|
|
|
func UpdateVertexAttribs(glProgram uint32, vertexSize int32) {
|
|
var sizeofFloat int32 = 4
|
|
stride := vertexSize * sizeofFloat
|
|
vertPos := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertPos\x00")))
|
|
gl.EnableVertexAttribArray(vertPos)
|
|
gl.VertexAttribPointerWithOffset(vertPos, 3, gl.FLOAT, false, stride, uintptr(0))
|
|
|
|
vertNormal := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertNormal\x00")))
|
|
gl.EnableVertexAttribArray(vertNormal)
|
|
gl.VertexAttribPointerWithOffset(vertNormal, 3, gl.FLOAT, false, stride, uintptr(3*sizeofFloat))
|
|
|
|
texCoordAttrib := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertTexCoord\x00")))
|
|
gl.EnableVertexAttribArray(texCoordAttrib)
|
|
gl.VertexAttribPointerWithOffset(texCoordAttrib, 2, gl.FLOAT, false, stride, uintptr(6*sizeofFloat))
|
|
}
|
|
|
|
func GLStr(str string) *uint8 {
|
|
// Something about Strings going to OpenGL needs a null tagged on
|
|
// the end in addition to the special gl.Str() thing.
|
|
return gl.Str(str + "\x00")
|
|
}
|
|
|
|
func SetUniformInt(glProgram uint32, name string, i int32) {
|
|
location := gl.GetUniformLocation(glProgram, GLStr(name))
|
|
gl.Uniform1i(location, i)
|
|
}
|
|
|
|
func SetUniformFloat(glProgram uint32, name string, f float32) {
|
|
location := gl.GetUniformLocation(glProgram, GLStr(name))
|
|
gl.Uniform1f(location, f)
|
|
}
|
|
|
|
func SetUniformVec3f(glProgram uint32, name string, v mgl32.Vec3) {
|
|
location := gl.GetUniformLocation(glProgram, GLStr(name))
|
|
gl.Uniform3f(location, v.X(), v.Y(), v.Z())
|
|
}
|
|
|
|
func SetUniformVec4f(glProgram uint32, name string, v mgl32.Vec4) {
|
|
location := gl.GetUniformLocation(glProgram, GLStr(name))
|
|
gl.Uniform4f(location, v.X(), v.Y(), v.Z(), v.W())
|
|
}
|
|
|
|
func SetUniformMatrix4f(glProgram uint32, name string, m *mgl32.Mat4) {
|
|
location := gl.GetUniformLocation(glProgram, GLStr(name))
|
|
gl.UniformMatrix4fv(location, 1, false, &m[0])
|
|
}
|