312 lines
8.4 KiB
Go
312 lines
8.4 KiB
Go
//+build ignore
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/build"
|
|
"go/parser"
|
|
"go/token"
|
|
"io/ioutil"
|
|
"log"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
func main() {
|
|
// find the go files in the current directory
|
|
pkg, err := build.Default.ImportDir(".", 0)
|
|
if err != nil {
|
|
log.Fatalf("Could not process directory: %s", err)
|
|
}
|
|
|
|
vsMap := make(map[string]string)
|
|
fsMap := make(map[string]string)
|
|
|
|
// go through each file and find const raw string literals with
|
|
fset := token.NewFileSet()
|
|
for _, goFile := range pkg.GoFiles {
|
|
parsedFile, err := parser.ParseFile(fset, goFile, nil, 0)
|
|
if err != nil {
|
|
log.Fatalf("Failed to parse file %s: %s", goFile, err)
|
|
}
|
|
|
|
for _, obj := range parsedFile.Scope.Objects {
|
|
isVS := strings.HasSuffix(obj.Name, "VS")
|
|
isFS := strings.HasSuffix(obj.Name, "FS")
|
|
var shaderCode string
|
|
if isVS || isFS {
|
|
if value, ok := obj.Decl.(*ast.ValueSpec); ok && len(value.Values) == 1 {
|
|
if lit, ok := value.Values[0].(*ast.BasicLit); ok {
|
|
shaderCode = lit.Value
|
|
}
|
|
}
|
|
}
|
|
if shaderCode != "" {
|
|
baseName := obj.Name[:len(obj.Name)-2]
|
|
if isVS {
|
|
vsMap[baseName] = shaderCode
|
|
} else if isFS {
|
|
fsMap[baseName] = shaderCode
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for name := range vsMap {
|
|
if _, ok := fsMap[name]; !ok {
|
|
log.Println("Warning: Vertex shader with no corresponding fragment shader (" + name + ")")
|
|
delete(vsMap, name)
|
|
continue
|
|
}
|
|
}
|
|
for name := range fsMap {
|
|
if _, ok := vsMap[name]; !ok {
|
|
log.Println("Warning: Fragment shader with no corresponding vertex shader (" + name + ")")
|
|
delete(fsMap, name)
|
|
continue
|
|
}
|
|
}
|
|
|
|
goCode := &bytes.Buffer{}
|
|
buildCodeHeader(goCode)
|
|
|
|
for name, vs := range vsMap {
|
|
fs := fsMap[name]
|
|
|
|
vs = vs[1 : len(vs)-1]
|
|
fs = fs[1 : len(fs)-1]
|
|
|
|
inputs := shaderFindInputVariables(vs + "\n" + fs)
|
|
|
|
buildCode(goCode, name, inputs)
|
|
}
|
|
|
|
err = ioutil.WriteFile("made_shaders.go", goCode.Bytes(), 0777)
|
|
if err != nil {
|
|
log.Fatalf("Could not write made_shaders.go: %s", err)
|
|
}
|
|
}
|
|
|
|
type ShaderInput struct {
|
|
Name string
|
|
IsAttrib bool
|
|
IsUniform bool
|
|
}
|
|
|
|
func shaderFindInputVariables(source string) []ShaderInput {
|
|
inputs := make([]ShaderInput, 0, 10)
|
|
|
|
varDefSplitter := regexp.MustCompile("[ \t\r\n,]+")
|
|
lines := shaderGetTopLevelLines(source)
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if len(line) == 0 || line[0] == '#' {
|
|
continue
|
|
}
|
|
if strings.Contains(line, "{") {
|
|
break
|
|
}
|
|
parts := varDefSplitter.Split(line, -1)
|
|
isAttrib := parts[0] == "attribute"
|
|
isUniform := parts[0] == "uniform"
|
|
if !isAttrib && !isUniform {
|
|
continue
|
|
}
|
|
for _, part := range parts[2:] {
|
|
if idx := strings.IndexByte(part, '['); idx >= 0 {
|
|
part = part[:idx]
|
|
}
|
|
inputs = append(inputs, ShaderInput{
|
|
Name: part,
|
|
IsAttrib: isAttrib,
|
|
IsUniform: isUniform})
|
|
}
|
|
}
|
|
return inputs
|
|
}
|
|
|
|
func shaderGetTopLevelLines(source string) []string {
|
|
sourceBytes := []byte(source)
|
|
l := len(sourceBytes)
|
|
if l == 0 {
|
|
return make([]string, 0)
|
|
}
|
|
|
|
var inPrecompiledStatement, inLineComment, inBlockComment, inString, stringEscape, lastWasWhitespace bool
|
|
curlyBraceDepth := 0
|
|
|
|
topLevelLines := make([]string, 0, 100)
|
|
currentLine := make([]byte, 0, 1000)
|
|
|
|
var c0, c1 byte = ' ', ' '
|
|
for i := 0; i < l; i++ {
|
|
c1 = sourceBytes[i]
|
|
isWhitespace := unicode.IsSpace(rune(c1))
|
|
if !inBlockComment && !inString && c0 == '/' && c1 == '/' {
|
|
inLineComment = true
|
|
if len(currentLine) > 0 {
|
|
currentLine = currentLine[:len(currentLine)-1]
|
|
}
|
|
} else if !inBlockComment && !inString && c1 == '#' {
|
|
inPrecompiledStatement = true
|
|
} else if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && c0 == '/' && c1 == '*' {
|
|
inBlockComment = true
|
|
if len(currentLine) > 0 {
|
|
currentLine = currentLine[:len(currentLine)-1]
|
|
}
|
|
} else if !inBlockComment && !inString && !inLineComment && !inPrecompiledStatement && c1 == '"' {
|
|
inString = true
|
|
} else if inString && !stringEscape && c1 == '\\' {
|
|
stringEscape = true
|
|
} else if inString && stringEscape {
|
|
stringEscape = false
|
|
} else if inString && !stringEscape && c1 == '"' {
|
|
inString = false
|
|
} else if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && c1 == '{' {
|
|
if curlyBraceDepth == 0 {
|
|
topLevelLines = append(topLevelLines, string(currentLine))
|
|
currentLine = currentLine[:0]
|
|
}
|
|
curlyBraceDepth++
|
|
}
|
|
if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && curlyBraceDepth == 0 {
|
|
if c1 == ';' {
|
|
topLevelLines = append(topLevelLines, string(currentLine))
|
|
currentLine = currentLine[:0]
|
|
} else if !isWhitespace {
|
|
currentLine = append(currentLine, c1)
|
|
} else if !lastWasWhitespace {
|
|
currentLine = append(currentLine, ' ')
|
|
}
|
|
}
|
|
if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && c1 == '}' {
|
|
curlyBraceDepth--
|
|
} else if (inLineComment || inPrecompiledStatement) && (c1 == '\r' || c1 == '\n') {
|
|
inLineComment = false
|
|
inPrecompiledStatement = false
|
|
} else if inBlockComment && c0 == '*' && c1 == '/' {
|
|
inBlockComment = false
|
|
}
|
|
|
|
lastWasWhitespace = isWhitespace
|
|
c0 = c1
|
|
}
|
|
|
|
topLevelLines = append(topLevelLines, string(currentLine))
|
|
|
|
return topLevelLines
|
|
}
|
|
|
|
const compilePart = `
|
|
{
|
|
SHADER_VAR = gli.CreateShader(gl_SHADER_TYPE)
|
|
gli.ShaderSource(SHADER_VAR, SHADER_SRC)
|
|
gli.CompileShader(SHADER_VAR)
|
|
|
|
shLog := gli.GetShaderInfoLog(SHADER_VAR)
|
|
if len(shLog) > 0 {
|
|
fmt.Printf("SHADER_TYPE compilation log for SHADER_SRC:\n\n%s\n", shLog)
|
|
}
|
|
|
|
var status int32
|
|
gli.GetShaderiv(SHADER_VAR, gl_COMPILE_STATUS, &status)
|
|
if status != gl_TRUE {
|
|
gli.DeleteShader(SHADER_VAR)
|
|
return nil, errors.New("Error compiling GL_SHADER_TYPE shader part for SHADER_SRC")
|
|
}
|
|
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
|
|
return nil, errors.New("error compiling shader part for SHADER_SRC, glError: " + fmt.Sprint(glErr))
|
|
}
|
|
}
|
|
`
|
|
const linkPart = `
|
|
{
|
|
program = gli.CreateProgram()
|
|
gli.AttachShader(program, vs)
|
|
gli.AttachShader(program, fs)
|
|
gli.LinkProgram(program)
|
|
|
|
shLog := gli.GetProgramInfoLog(program)
|
|
if len(shLog) > 0 {
|
|
fmt.Printf("Shader link log for SHADER_SRC:\n\n%s\n", shLog)
|
|
}
|
|
|
|
var status int32
|
|
gli.GetProgramiv(program, gl_LINK_STATUS, &status)
|
|
if status != gl_TRUE {
|
|
gli.DeleteShader(vs)
|
|
gli.DeleteShader(fs)
|
|
return nil, errors.New("error linking shader for SHADER_SRC")
|
|
}
|
|
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
|
|
return nil, errors.New("error linking shader for SHADER_SRC, glError: " + fmt.Sprint(glErr))
|
|
}
|
|
}
|
|
`
|
|
|
|
func capitalize(s string) string {
|
|
return strings.ToUpper(s[:1]) + s[1:]
|
|
}
|
|
|
|
func buildCodeHeader(buf *bytes.Buffer) {
|
|
fmt.Fprint(buf, "package canvas\n\n")
|
|
fmt.Fprint(buf, "import (\n")
|
|
fmt.Fprint(buf, "\t\"errors\"\n")
|
|
fmt.Fprint(buf, "\t\"fmt\"\n")
|
|
fmt.Fprint(buf, ")\n\n")
|
|
}
|
|
|
|
func buildCode(buf *bytes.Buffer, baseName string, inputs []ShaderInput) {
|
|
shaderName := baseName + "Shader"
|
|
vsName := baseName + "VS"
|
|
fsName := baseName + "FS"
|
|
|
|
fmt.Fprintf(buf, "type %s struct {\n", shaderName)
|
|
fmt.Fprint(buf, "\tid uint32\n")
|
|
for _, input := range inputs {
|
|
if input.IsAttrib {
|
|
fmt.Fprintf(buf, "\t%s uint32\n", input.Name)
|
|
} else if input.IsUniform {
|
|
fmt.Fprintf(buf, "\t%s int32\n", input.Name)
|
|
}
|
|
}
|
|
fmt.Fprint(buf, "}\n\n")
|
|
fmt.Fprintf(buf, "func load%s() (*%s, error) {\n", capitalize(shaderName), shaderName)
|
|
fmt.Fprint(buf, "\tvar vs, fs, program uint32\n")
|
|
|
|
var part string
|
|
|
|
part = strings.Replace(compilePart, "SHADER_SRC", vsName, -1)
|
|
part = strings.Replace(part, "SHADER_TYPE", "VERTEX_SHADER", -1)
|
|
part = strings.Replace(part, "SHADER_VAR", "vs", -1)
|
|
fmt.Fprint(buf, part)
|
|
|
|
part = strings.Replace(compilePart, "SHADER_SRC", fsName, -1)
|
|
part = strings.Replace(part, "SHADER_TYPE", "FRAGMENT_SHADER", -1)
|
|
part = strings.Replace(part, "SHADER_VAR", "fs", -1)
|
|
fmt.Fprint(buf, part)
|
|
|
|
part = strings.Replace(linkPart, "SHADER_SRC", fsName, -1)
|
|
fmt.Fprint(buf, part)
|
|
|
|
fmt.Fprint(buf, "\n")
|
|
fmt.Fprintf(buf, "\tresult := &%s{}\n", shaderName)
|
|
fmt.Fprint(buf, "\tresult.id = program\n")
|
|
|
|
for _, input := range inputs {
|
|
if input.IsAttrib {
|
|
fmt.Fprintf(buf, "\tresult.%s = uint32(gli.GetAttribLocation(program, \"%s\"))\n", input.Name, input.Name)
|
|
} else if input.IsUniform {
|
|
fmt.Fprintf(buf, "\tresult.%s = gli.GetUniformLocation(program, \"%s\")\n", input.Name, input.Name)
|
|
}
|
|
}
|
|
|
|
fmt.Fprint(buf, "\n")
|
|
|
|
fmt.Fprint(buf, "\treturn result, nil\n")
|
|
fmt.Fprint(buf, "}\n")
|
|
}
|