carpy-breakout/pkg/glshader/glshader.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])
}