initial commit
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package shaderManager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
)
|
||||
|
||||
// Shader represents a compiled GLSL program and its associated data.
|
||||
type Shader struct {
|
||||
Name string
|
||||
ProgramID uint32
|
||||
Render func() // This will be used in the future.
|
||||
|
||||
// Cached uniform locations (looked up once after linking)
|
||||
LocMVPMatrix int32
|
||||
LocTexture int32
|
||||
LocTextColor int32
|
||||
LocColor int32
|
||||
}
|
||||
|
||||
// ShaderManager handles the loading, compiling, and management of GLSL shader programs.
|
||||
type ShaderManager struct {
|
||||
shaders map[string]*Shader
|
||||
}
|
||||
|
||||
var (
|
||||
instance *ShaderManager
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// InitShaderManager initializes the ShaderManager singleton.
|
||||
func InitShaderManager(shaderDir string) error {
|
||||
var err error
|
||||
once.Do(func() {
|
||||
sm := &ShaderManager{
|
||||
shaders: make(map[string]*Shader),
|
||||
}
|
||||
if e := sm.LoadShaders(shaderDir); e != nil {
|
||||
err = fmt.Errorf("could not load shaders from %s: %w", shaderDir, e)
|
||||
return
|
||||
}
|
||||
instance = sm
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns the singleton ShaderManager instance.
|
||||
func Get() *ShaderManager {
|
||||
if instance == nil {
|
||||
panic("ShaderManager has not been initialized. Call InitShaderManager at application startup.")
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
// Get retrieves a compiled Shader object by its name.
|
||||
func (sm *ShaderManager) Get(name string) (*Shader, bool) {
|
||||
shader, ok := sm.shaders[name]
|
||||
return shader, ok
|
||||
}
|
||||
|
||||
// ListShaders returns all shaders.
|
||||
func (sm *ShaderManager) ListShaders() map[string]*Shader {
|
||||
return sm.shaders
|
||||
}
|
||||
|
||||
// Close deletes all loaded shader programs from the GPU to free up resources.
|
||||
func (sm *ShaderManager) Close() {
|
||||
for name, shader := range sm.shaders {
|
||||
gl.DeleteProgram(shader.ProgramID)
|
||||
delete(sm.shaders, name)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadShaders scans a directory for .vert and .frag files and creates a shader program for each pair.
|
||||
func (sm *ShaderManager) LoadShaders(shaderDir string) error {
|
||||
// Get assets directory from database
|
||||
shaderFiles := make(map[string]string)
|
||||
|
||||
err := filepath.Walk(shaderDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && strings.HasSuffix(info.Name(), ".vert") {
|
||||
name := strings.TrimSuffix(info.Name(), ".vert")
|
||||
shaderFiles[name] = path
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error walking shader directory: %w", err)
|
||||
}
|
||||
|
||||
for name, vertPath := range shaderFiles {
|
||||
fragPath := strings.Replace(vertPath, ".vert", ".frag", 1)
|
||||
if _, err := os.Stat(fragPath); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
programID, err := createShaderProgram(vertPath, fragPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newShader := &Shader{
|
||||
Name: name,
|
||||
ProgramID: programID,
|
||||
Render: nil, // Render function is not yet assigned.
|
||||
}
|
||||
newShader.cacheUniformLocations()
|
||||
sm.shaders[name] = newShader
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cacheUniformLocations looks up common uniform locations once and stores them on the Shader.
|
||||
func (s *Shader) cacheUniformLocations() {
|
||||
s.LocMVPMatrix = gl.GetUniformLocation(s.ProgramID, gl.Str("u_mvpMatrix\x00"))
|
||||
s.LocTexture = gl.GetUniformLocation(s.ProgramID, gl.Str("u_texture\x00"))
|
||||
s.LocTextColor = gl.GetUniformLocation(s.ProgramID, gl.Str("u_textColor\x00"))
|
||||
s.LocColor = gl.GetUniformLocation(s.ProgramID, gl.Str("u_color\x00"))
|
||||
}
|
||||
|
||||
// createShaderProgram reads, compiles, and links a vertex and fragment shader from file paths.
|
||||
func createShaderProgram(vertexPath, fragmentPath string) (uint32, error) {
|
||||
vertexSource, err := os.ReadFile(vertexPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading vertex shader %s: %w", vertexPath, err)
|
||||
}
|
||||
fragmentSource, err := os.ReadFile(fragmentPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading fragment shader %s: %w", fragmentPath, err)
|
||||
}
|
||||
|
||||
vertexShader, err := compileShader(string(vertexSource)+"\x00", gl.VERTEX_SHADER)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer gl.DeleteShader(vertexShader)
|
||||
|
||||
fragmentShader, err := compileShader(string(fragmentSource)+"\x00", gl.FRAGMENT_SHADER)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer gl.DeleteShader(fragmentShader)
|
||||
|
||||
program := gl.CreateProgram()
|
||||
gl.AttachShader(program, vertexShader)
|
||||
gl.AttachShader(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
|
||||
}
|
||||
|
||||
// compileShader is a helper function that compiles a single shader source string.
|
||||
func compileShader(source string, shaderType uint32) (uint32, error) {
|
||||
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)
|
||||
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))
|
||||
|
||||
var shaderTypeName string
|
||||
switch shaderType {
|
||||
case gl.VERTEX_SHADER:
|
||||
shaderTypeName = "Vertex"
|
||||
case gl.FRAGMENT_SHADER:
|
||||
shaderTypeName = "Fragment"
|
||||
default:
|
||||
shaderTypeName = "Unknown"
|
||||
}
|
||||
return 0, fmt.Errorf("failed to compile %s shader: %v", shaderTypeName, log)
|
||||
}
|
||||
|
||||
return shader, nil
|
||||
}
|
||||
Reference in New Issue
Block a user