258 lines
6.3 KiB
Go
258 lines
6.3 KiB
Go
package render
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
"edgaru089.ml/go/gl01/internal/util/itype"
|
|
"github.com/go-gl/gl/all-core/gl"
|
|
"github.com/go-gl/mathgl/mgl32"
|
|
)
|
|
|
|
// returns max texture unit count
|
|
func getMaxTextureUnits() int32 {
|
|
var cnt int32
|
|
gl.GetIntegerv(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS, &cnt)
|
|
return cnt
|
|
}
|
|
|
|
// Shader contains a shader program, with a vertex and a fragment (pixel) shader.
|
|
type Shader struct {
|
|
prog uint32
|
|
uniforms map[string]int32
|
|
textures map[int32]*Texture // maps uniform location to *Texture
|
|
}
|
|
|
|
// helper construct to get uniforms and restore previous glUseProgram
|
|
func (s *Shader) uniformBlender(name string) (location int32, restore func()) {
|
|
if s.prog != 0 {
|
|
var saved int32
|
|
// Use program object
|
|
gl.GetIntegerv(gl.CURRENT_PROGRAM, &saved)
|
|
if uint32(saved) != s.prog {
|
|
gl.UseProgram(s.prog)
|
|
}
|
|
|
|
return s.UniformLocation(name), func() {
|
|
if uint32(saved) != s.prog {
|
|
gl.UseProgram(uint32(saved))
|
|
}
|
|
}
|
|
}
|
|
return 0, func() {}
|
|
}
|
|
|
|
func compileShader(src string, stype uint32) (prog uint32, err error) {
|
|
prog = gl.CreateShader(stype)
|
|
|
|
strs, free := gl.Strs(src, "\x00")
|
|
gl.ShaderSource(prog, 1, strs, nil)
|
|
free()
|
|
gl.CompileShader(prog)
|
|
|
|
var status int32
|
|
gl.GetShaderiv(prog, gl.COMPILE_STATUS, &status)
|
|
if status == gl.FALSE {
|
|
var len int32
|
|
gl.GetShaderiv(prog, gl.INFO_LOG_LENGTH, &len)
|
|
|
|
log := strings.Repeat("\x00", int(len+1))
|
|
gl.GetShaderInfoLog(prog, len, nil, gl.Str(log))
|
|
|
|
gl.DeleteShader(prog)
|
|
|
|
switch stype {
|
|
case gl.VERTEX_SHADER:
|
|
return 0, fmt.Errorf("failed to compile Vertex Shader: %s", log)
|
|
case gl.FRAGMENT_SHADER:
|
|
return 0, fmt.Errorf("failed to compile Fragment Shader: %s", log)
|
|
default:
|
|
return 0, fmt.Errorf("failed to compile Unknown(%d) Shader: %s", stype, log)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// NewShader compiles and links a Vertex and a Fragment (Pixel) shader into one program.
|
|
// The source code does not need to be terminated with \x00.
|
|
func NewShader(vert, frag string) (s *Shader, err error) {
|
|
|
|
vertid, err := compileShader(vert, gl.VERTEX_SHADER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fragid, err := compileShader(frag, gl.FRAGMENT_SHADER)
|
|
if err != nil {
|
|
gl.DeleteShader(vertid)
|
|
return nil, err
|
|
}
|
|
|
|
s = &Shader{}
|
|
s.uniforms = make(map[string]int32)
|
|
s.textures = make(map[int32]*Texture)
|
|
s.prog = gl.CreateProgram()
|
|
|
|
gl.AttachShader(s.prog, vertid)
|
|
gl.AttachShader(s.prog, fragid)
|
|
gl.LinkProgram(s.prog)
|
|
|
|
var status int32
|
|
gl.GetProgramiv(s.prog, gl.LINK_STATUS, &status)
|
|
if status == gl.FALSE {
|
|
var len int32
|
|
gl.GetProgramiv(s.prog, gl.INFO_LOG_LENGTH, &len)
|
|
|
|
log := strings.Repeat("\x00", int(len+1))
|
|
gl.GetProgramInfoLog(s.prog, len, nil, gl.Str(log))
|
|
|
|
gl.DeleteProgram(s.prog)
|
|
gl.DeleteShader(vertid)
|
|
gl.DeleteShader(fragid)
|
|
|
|
return nil, fmt.Errorf("failed to link Program: %s", log)
|
|
}
|
|
|
|
gl.DeleteShader(vertid)
|
|
gl.DeleteShader(fragid)
|
|
|
|
return
|
|
}
|
|
|
|
// UniformLocation returns the location id of the given uniform.
|
|
// it returns -1 if the uniform is not found.
|
|
func (s *Shader) UniformLocation(name string) int32 {
|
|
if id, ok := s.uniforms[name]; ok {
|
|
return id
|
|
} else {
|
|
|
|
location := gl.GetUniformLocation(s.prog, gl.Str(name+"\x00"))
|
|
s.uniforms[name] = location
|
|
|
|
if location == -1 {
|
|
log.Printf("Shader: uniform \"%s\" not found", name)
|
|
}
|
|
|
|
return location
|
|
}
|
|
}
|
|
|
|
// UseProgram calls glUseProgram.
|
|
func (s *Shader) UseProgram() {
|
|
gl.UseProgram(s.prog)
|
|
}
|
|
|
|
// BindTextures calls glActiveTexture and glBindTexture, updating the texture unit slots.
|
|
func (s *Shader) BindTextures() {
|
|
var i int
|
|
for loc, tex := range s.textures {
|
|
|
|
index := int32(i + 1)
|
|
|
|
gl.Uniform1i(loc, index)
|
|
gl.ActiveTexture(uint32(gl.TEXTURE0 + index))
|
|
gl.BindTexture(gl.TEXTURE_2D, tex.tex)
|
|
i++
|
|
}
|
|
|
|
gl.ActiveTexture(gl.TEXTURE0)
|
|
}
|
|
|
|
// Handle returns the OpenGL handle of the program.
|
|
func (s *Shader) Handle() uint32 {
|
|
return s.prog
|
|
}
|
|
|
|
func (s *Shader) SetUniformTexture(name string, tex *Texture) {
|
|
if s.prog == 0 {
|
|
return
|
|
}
|
|
|
|
loc := s.UniformLocation(name)
|
|
if loc == -1 {
|
|
return
|
|
}
|
|
|
|
// Store the location to texture map
|
|
_, ok := s.textures[loc]
|
|
if !ok {
|
|
// new texture, make sure there are enough texture units
|
|
if len(s.textures)+1 >= int(getMaxTextureUnits()) {
|
|
log.Printf("Shader: Warning: Impossible to use texture \"%s\" for shader: all available texture units are used", name)
|
|
return
|
|
}
|
|
}
|
|
|
|
s.textures[loc] = tex
|
|
}
|
|
|
|
// SetUniformTextureHandle sets a uniform as a sampler2D from an external OpenGL texture.
|
|
// tex is the OpenGL texture handle (the one you get with glGenTextures())
|
|
func (s *Shader) SetUniformTextureHandle(name string, tex uint32) {
|
|
s.SetUniformTexture(name, &Texture{tex: tex})
|
|
}
|
|
|
|
func (s *Shader) SetUniformMat4(name string, value mgl32.Mat4) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.UniformMatrix4fv(loc, 1, false, &value[0])
|
|
}
|
|
|
|
func (s *Shader) SetUniformFloat(name string, value float32) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform1f(loc, value)
|
|
}
|
|
func (s *Shader) SetUniformVec2f(name string, value itype.Vec2f) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform2f(loc, value[0], value[1])
|
|
}
|
|
func (s *Shader) SetUniformVec3f(name string, value itype.Vec3f) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform3f(loc, value[0], value[1], value[2])
|
|
}
|
|
func (s *Shader) SetUniformVec4f(name string, value itype.Vec4f) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform4f(loc, value[0], value[1], value[2], value[3])
|
|
}
|
|
|
|
func (s *Shader) SetUniformInt(name string, value int32) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform1i(loc, value)
|
|
}
|
|
func (s *Shader) SetUniformVec2i(name string, value itype.Vec2i) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform2i(loc, int32(value[0]), int32(value[1]))
|
|
}
|
|
func (s *Shader) SetUniformVec3i(name string, value itype.Vec3i) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform3i(loc, int32(value[0]), int32(value[1]), int32(value[2]))
|
|
}
|
|
func (s *Shader) SetUniformVec4i(name string, value itype.Vec4i) {
|
|
loc, restore := s.uniformBlender(name)
|
|
defer restore()
|
|
|
|
gl.Uniform4i(loc, int32(value[0]), int32(value[1]), int32(value[2]), int32(value[3]))
|
|
}
|
|
|
|
func (s *Shader) GetAttribLocation(name string) uint32 {
|
|
name = name + "\x00"
|
|
return uint32(gl.GetAttribLocation(s.prog, gl.Str(name)))
|
|
}
|