| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package gls
- import (
- "bytes"
- "errors"
- "fmt"
- "github.com/g3n/engine/math32"
- "github.com/go-gl/gl/v3.3-core/gl"
- "io"
- "strconv"
- "strings"
- )
- // Shader Program Object
- type Program struct {
- // Shows source code in error messages
- ShowSource bool
- gs *GLS
- handle uint32
- shaders []shaderInfo
- uniforms map[string]int32
- Specs interface{}
- }
- type shaderInfo struct {
- stype uint32
- source string
- defines map[string]interface{}
- handle uint32
- }
- // Map shader types to names
- var shaderNames = map[uint32]string{
- gl.VERTEX_SHADER: "Vertex Shader",
- gl.FRAGMENT_SHADER: "Fragment Shader",
- }
- // NewProgram creates a new empty shader program object.
- // Use this type methods to add shaders and build the final program.
- func (gs *GLS) NewProgram() *Program {
- prog := new(Program)
- prog.gs = gs
- prog.shaders = make([]shaderInfo, 0)
- prog.uniforms = make(map[string]int32)
- prog.ShowSource = true
- return prog
- }
- // AddShaders adds a shader to this program.
- // This must be done before the program is built.
- func (prog *Program) AddShader(stype uint32, source string, defines map[string]interface{}) {
- if prog.handle != 0 {
- log.Fatal("Program already built")
- }
- prog.shaders = append(prog.shaders, shaderInfo{stype, source, defines, 0})
- }
- // Build builds the program compiling and linking the previously supplied shaders.
- func (prog *Program) Build() error {
- if prog.handle != 0 {
- return fmt.Errorf("Program already built")
- }
- // Checks if shaders were provided
- if len(prog.shaders) == 0 {
- return fmt.Errorf("No shaders supplied")
- }
- // Create program
- prog.handle = gl.CreateProgram()
- if prog.handle == 0 {
- return fmt.Errorf("Error creating program")
- }
- // Clean unused GL allocated resources
- defer func() {
- for _, sinfo := range prog.shaders {
- if sinfo.handle != 0 {
- gl.DeleteShader(sinfo.handle)
- sinfo.handle = 0
- }
- }
- }()
- // Compiles and attach each shader
- for _, sinfo := range prog.shaders {
- // Creates string with defines from specified parameters
- deflines := make([]string, 0)
- if sinfo.defines != nil {
- for pname, pval := range sinfo.defines {
- line := "#define " + pname + " "
- switch val := pval.(type) {
- case bool:
- if val {
- deflines = append(deflines, line)
- }
- case float32:
- line += strconv.FormatFloat(float64(val), 'f', -1, 32)
- deflines = append(deflines, line)
- default:
- panic("Parameter type not supported")
- }
- }
- }
- deftext := strings.Join(deflines, "\n")
- // Compile shader
- shader, err := CompileShader(sinfo.stype, sinfo.source+deftext)
- if err != nil {
- gl.DeleteProgram(prog.handle)
- prog.handle = 0
- msg := fmt.Sprintf("Error compiling %s: %s", shaderNames[sinfo.stype], err)
- if prog.ShowSource {
- source := FormatSource(sinfo.source + deftext)
- msg += source
- }
- return errors.New(msg)
- }
- sinfo.handle = shader
- gl.AttachShader(prog.handle, shader)
- }
- // Link program and checks for errors
- gl.LinkProgram(prog.handle)
- var status int32
- gl.GetProgramiv(prog.handle, gl.LINK_STATUS, &status)
- if status == gl.FALSE {
- var logLength int32
- gl.GetProgramiv(prog.handle, gl.INFO_LOG_LENGTH, &logLength)
- log := strings.Repeat("\x00", int(logLength+1))
- gl.GetProgramInfoLog(prog.handle, logLength, nil, gl.Str(log))
- prog.handle = 0
- return fmt.Errorf("Error linking program: %v", log)
- }
- return nil
- }
- // Handle returns the handle of this program
- func (prog *Program) Handle() uint32 {
- return prog.handle
- }
- // GetActiveUniformBlockSize returns the minimum number of bytes
- // to contain the data for the uniform block specified by its index.
- func (prog *Program) GetActiveUniformBlockSize(ubindex uint32) int32 {
- var uboSize int32
- gl.GetActiveUniformBlockiv(prog.handle, ubindex, gl.UNIFORM_BLOCK_DATA_SIZE, &uboSize)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("GetUniformBlockSize(%v) error: %d", ubindex, ecode)
- }
- }
- return uboSize
- }
- // GetActiveUniformsiv returns information about the specified uniforms
- // specified by its indices
- func (prog *Program) GetActiveUniformsiv(indices []uint32, pname uint32) []int32 {
- data := make([]int32, len(indices))
- gl.GetActiveUniformsiv(prog.handle, int32(len(indices)), &indices[0], pname, &data[0])
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("GetActiveUniformsiv() error: %d", ecode)
- }
- }
- return data
- }
- // GetAttributeLocation returns the location of the specified attribute
- // in this program. This location is internally cached.
- func (prog *Program) GetAttribLocation(name string) int32 {
- loc := gl.GetAttribLocation(prog.handle, gl.Str(name+"\x00"))
- prog.gs.checkError("GetAttribLocation")
- return loc
- }
- // GetUniformBlockIndex returns the index of the named uniform block.
- // If the supplied name is not valid, the function returns gl.INVALID_INDEX
- func (prog *Program) GetUniformBlockIndex(name string) uint32 {
- index := gl.GetUniformBlockIndex(prog.handle, gl.Str(name+"\x00"))
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("GetUniformBlockIndex(%s) error", name)
- }
- }
- return index
- }
- // GetUniformIndices returns the indices for each specified named
- // uniform. If an specified name is not valid the corresponding
- // index value will be gl.INVALID_INDEX
- func (prog *Program) GetUniformIndices(names []string) []uint32 {
- // Add C terminators to uniform names
- for _, s := range names {
- s += "\x00"
- }
- unames, freefunc := gl.Strs(names...)
- indices := make([]uint32, len(names))
- gl.GetUniformIndices(prog.handle, int32(len(names)), unames, &indices[0])
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("GetUniformIndices() error: %d", ecode)
- }
- }
- freefunc()
- return indices
- }
- // GetUniformLocation returns the location of the specified uniform in this program.
- // This location is internally cached.
- func (prog *Program) GetUniformLocation(name string) int32 {
- // Try to get from the cache
- loc, ok := prog.uniforms[name]
- if ok {
- return loc
- }
- // Get location from GL
- loc = gl.GetUniformLocation(prog.handle, gl.Str(name+"\x00"))
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("GetUniformLocation(%s) error: %d", name, ecode)
- }
- }
- // Cache result
- prog.uniforms[name] = loc
- if loc < 0 {
- log.Warn("GetUniformLocation(%s) NOT FOUND", name)
- }
- return loc
- }
- // SetUniformInt sets this program uniform variable specified by
- // its location to the the value of the specified int
- func (prog *Program) SetUniformInt(loc int32, v int) {
- gl.Uniform1i(loc, int32(v))
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformInt() error: %d", ecode)
- }
- }
- }
- // SetUniformFloat sets this program uniform variable specified by
- // its location to the the value of the specified float
- func (prog *Program) SetUniformFloat(loc int32, v float32) {
- gl.Uniform1f(loc, v)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformFloat() error: %d", ecode)
- }
- }
- }
- // SetUniformVector2 sets this program uniform variable specified by
- // its location to the the value of the specified Vector2
- func (prog *Program) SetUniformVector2(loc int32, v *math32.Vector2) {
- gl.Uniform2f(loc, v.X, v.Y)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformVector2() error: %d", ecode)
- }
- }
- }
- // SetUniformVector3 sets this program uniform variable specified by
- // its location to the the value of the specified Vector3
- func (prog *Program) SetUniformVector3(loc int32, v *math32.Vector3) {
- gl.Uniform3f(loc, v.X, v.Y, v.Z)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformVector3() error: %d", ecode)
- }
- }
- }
- // SetUniformVector4 sets this program uniform variable specified by
- // its location to the the value of the specified Vector4
- func (prog *Program) SetUniformVector4(loc int32, v *math32.Vector4) {
- gl.Uniform4f(loc, v.X, v.Y, v.Z, v.W)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformVector4() error: %d", ecode)
- }
- }
- }
- // SetUniformMatrix3 sets this program uniform variable specified by
- // its location with the values from the specified Matrix3.
- func (prog *Program) SetUniformMatrix3(loc int32, m *math32.Matrix3) {
- gl.UniformMatrix3fv(loc, 1, false, &m[0])
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformMatrix3() error: %d", ecode)
- }
- }
- }
- // SetUniformMatrix4 sets this program uniform variable specified by
- // its location with the values from the specified Matrix4.
- func (prog *Program) SetUniformMatrix4(loc int32, m *math32.Matrix4) {
- gl.UniformMatrix4fv(loc, 1, false, &m[0])
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformMatrix4() error: %d", ecode)
- }
- }
- }
- // SetUniformIntByName sets this program uniform variable specified by
- // its name to the value of the specified int.
- // The specified name location is cached internally.
- func (prog *Program) SetUniformIntByName(name string, v int) {
- gl.Uniform1i(prog.GetUniformLocation(name), int32(v))
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("GetUniformIntByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformFloatByName sets this program uniform variable specified by
- // its name to the value of the specified float32.
- // The specified name location is cached internally.
- func (prog *Program) SetUniformFloatByName(name string, v float32) {
- gl.Uniform1f(prog.GetUniformLocation(name), v)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformFloatByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformVector2ByName sets this program uniform variable specified by
- // its name to the values from the specified Vector2.
- // The specified name location is cached internally.
- func (prog *Program) SetUniformVector2ByName(name string, v *math32.Vector2) {
- gl.Uniform2f(prog.GetUniformLocation(name), v.X, v.Y)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformVector2ByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformVector3ByName sets this program uniform variable specified by
- // its name to the values from the specified Vector3.
- // The specified name location is cached internally.
- func (prog *Program) SetUniformVector3ByName(name string, v *math32.Vector3) {
- gl.Uniform3f(prog.GetUniformLocation(name), v.X, v.Y, v.Z)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformVector3ByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformVector4ByName sets this program uniform variable specified by
- // its name to the values from the specified Vector4.
- // The specified name location is cached internally.
- func (prog *Program) SetUniformVector4ByName(name string, v *math32.Vector4) {
- gl.Uniform4f(prog.GetUniformLocation(name), v.X, v.Y, v.Z, v.W)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformVector4ByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformMatrix3ByName sets this program uniform variable specified by
- // its name with the values from the specified Matrix3.
- // The specified name location is cached internally.
- func (prog *Program) SetUniformMatrix3ByName(name string, m *math32.Matrix3) {
- gl.UniformMatrix3fv(prog.GetUniformLocation(name), 1, false, &m[0])
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformMatrix3ByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformMatrix4ByName sets this program uniform variable specified by
- // its name with the values from the specified Matrix4.
- // The location of the name is cached internally.
- func (prog *Program) SetUniformMatrix4ByName(name string, m *math32.Matrix4) {
- gl.UniformMatrix4fv(prog.GetUniformLocation(name), 1, false, &m[0])
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformMatrix4ByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformColorByName set this program uniform variable specified by
- // its name to the values from the specified Color
- // The specified name location is cached internally.
- func (prog *Program) SetUniformColorByName(name string, c *math32.Color) {
- gl.Uniform3f(prog.GetUniformLocation(name), c.R, c.G, c.B)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformColorByName(%s) error: %d", name, ecode)
- }
- }
- }
- // SetUniformColor4ByName set this program uniform variable specified by
- // its name to the values from the specified Color4
- // The specified name location is cached internally.
- func (prog *Program) SetUniformColor4ByName(name string, c *math32.Color4) {
- gl.Uniform4f(prog.GetUniformLocation(name), c.R, c.G, c.B, c.A)
- if prog.gs.CheckErrors() {
- ecode := gl.GetError()
- if ecode != 0 {
- log.Fatal("SetUniformColor4ByName(%s) error: %d", name, ecode)
- }
- }
- }
- // CompileShader creates and compiles a shader of the specified type and with
- // the specified source code and returns a non-zero value by which
- // it can be referenced.
- func CompileShader(stype uint32, source string) (uint32, error) {
- shader := gl.CreateShader(stype)
- if shader == 0 {
- return 0, fmt.Errorf("Error creating shader")
- }
- // Allocates C string to store the source
- csource, freeSource := gl.Strs(source + "\x00")
- defer freeSource()
- // Set shader source and compile it
- gl.ShaderSource(shader, 1, csource, nil)
- gl.CompileShader(shader)
- // Get the shader compiler log
- var logLength int32
- gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
- slog := strings.Repeat("\x00", int(logLength+1))
- gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(slog))
- // Get the shader compile status
- var status int32
- gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
- if status == gl.FALSE {
- return shader, fmt.Errorf("%s", slog)
- }
- // If the shader compiled OK but the log has data,
- // logs this data instead of returning error
- if len(slog) > 2 {
- log.Warn("%s", slog)
- }
- return shader, nil
- }
- // FormatSource returns the supplied program source code with
- // line numbers prepended.
- func FormatSource(source string) string {
- // Reads all lines from the source string
- lines := make([]string, 0)
- buf := bytes.NewBuffer([]byte(source))
- for {
- line, err := buf.ReadBytes('\n')
- if err != nil {
- if err == io.EOF {
- break
- }
- panic(err)
- }
- lines = append(lines, string(line[:len(line)-1]))
- }
- // Adds a final line terminator
- lines = append(lines, "\n")
- // Prepends the line number for each line
- ndigits := len(strconv.Itoa(len(lines)))
- format := "%0" + strconv.Itoa(ndigits) + "d:%s"
- formatted := make([]string, 0)
- for pos, l := range lines {
- fline := fmt.Sprintf(format, pos+1, l)
- formatted = append(formatted, fline)
- }
- return strings.Join(formatted, "\n")
- }
|