| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // 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.
- // g3nshaders reads shaders files with ".glsl" extensions and generates
- // a Go file containing strings with the content of these files.
- // Also it builds maps associating include and shader names to its respective
- // source strings.
- // Usage:
- // g3nshaders -in=<input_dir> -out<output_gofile> -v
- // It is normally invoked by "go generate" inside the "shaders" directory
- package main
- import (
- "bytes"
- "flag"
- "fmt"
- "go/format"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "strings"
- "text/template"
- )
- // Program constants.
- const (
- PROGNAME = "g3nshaders"
- VMAJOR = 0
- VMINOR = 1
- SHADEREXT = ".glsl"
- DIR_INCLUDE = "include"
- TYPE_VERTEX = "vertex"
- TYPE_FRAGMENT = "fragment"
- TYPE_GEOMETRY = "geometry"
- )
- //
- // TEMPLATE is a Go template to generate the output file with the shaders' sources and
- // maps describing the include and shader names and programs shaders.
- //
- const TEMPLATE = `// Code generated by G3NSHADERS. DO NOT EDIT.
- // To regenerate this file install 'g3nshaders' and execute:
- // 'go generate' in this folder.
- package {{.Pkg}}
- {{range .Includes}}
- const include_{{.Name}}_source = ` + "`{{.Source}}`" + `
- {{end}}
- {{range .Shaders}}
- const {{.Name}}_source = ` + "`{{.Source}}`" + `
- {{end}}
- // Maps include name with its source code
- var includeMap = map[string]string {
- {{range .Includes}}
- "{{- .Name}}": include_{{.Name}}_source, {{end}}
- }
- // Maps shader name with its source code
- var shaderMap = map[string]string {
- {{range .Shaders}}
- "{{- .Name}}": {{.Name}}_source, {{end}}
- }
- // Maps program name with Proginfo struct with shaders names
- var programMap = map[string]ProgramInfo{
- {{ range $progName, $progInfo := .Programs }}
- "{{$progName}}": { "{{$progInfo.Vertex}}","{{$progInfo.Fragment}}","{{$progInfo.Geometry}}" }, {{end}}
- }
- `
- // Command line options
- var (
- oVersion = flag.Bool("version", false, "Show version and exits")
- oInp = flag.String("in", ".", "Input directory")
- oOut = flag.String("out", "sources.go", "Go output file")
- oPackage = flag.String("pkg", "shaders", "Package name")
- oVerbose = flag.Bool("v", false, "Show files being processed")
- )
- // Valid shader types
- var shaderTypes = map[string]bool{
- TYPE_VERTEX: true,
- TYPE_FRAGMENT: true,
- TYPE_GEOMETRY: true,
- }
- // fileInfo describes a shader or include file name and source code
- type fileInfo struct {
- Name string // shader or include name
- Source string // shader or include source code
- Include bool // true if include, false otherwise
- }
- // progInfo describes all the shader names of an specific program
- // If the program doesn't use the geometry shader it is set as an empty string
- type progInfo struct {
- Vertex string // vertex shader name
- Fragment string // fragment shader name
- Geometry string // geometry shader name
- }
- // templInfo contains all information needed for the template expansion
- type templInfo struct {
- Count int // number of shader files processed
- Pkg string // name of the package for the generated output file
- Includes []fileInfo // list of include files found
- Shaders []fileInfo // list of shader files found
- Programs map[string]progInfo // map of shader programs found
- }
- var templData templInfo // global template data
- var logger *log.Logger // global logger
- func main() {
- // Parse command line parameters
- flag.Usage = usage
- flag.Parse()
- // If requested, print version and exits
- if *oVersion == true {
- fmt.Fprintf(os.Stderr, "%s v%d.%d\n", PROGNAME, VMAJOR, VMINOR)
- return
- }
- // Creates logger
- logger = log.New(os.Stdout, "G3NSHADERS ", log.Ldate|log.Ltime)
- // Initialize template data
- templData.Pkg = *oPackage
- templData.Programs = make(map[string]progInfo)
- // Process the current directory and its subdirectories recursively
- // appending information into templData
- processDir(*oInp, false)
- if templData.Count == 0 {
- log.Print("No shader files found")
- return
- }
- // Generates output file from TEMPLATE
- generate(*oOut)
- }
- // processDir processes recursively all shaders files in the specified directory
- func processDir(dir string, include bool) {
- // Open directory to process
- f, err := os.Open(dir)
- if err != nil {
- panic(err)
- }
- defer f.Close()
- // Read all file entries from the directory
- finfos, err := f.Readdir(0)
- if err != nil {
- panic(err)
- }
- // Process all directory entries.
- for _, fi := range finfos {
- if fi.IsDir() {
- dirInclude := include
- if fi.Name() == DIR_INCLUDE {
- dirInclude = true
- }
- processDir(filepath.Join(dir, fi.Name()), dirInclude)
- } else {
- processFile(filepath.Join(dir, fi.Name()), include)
- }
- }
- }
- // processFile process one file checking if it has the shaders extension,
- // otherwise it is ignored.
- // If the include flag is true the file is an include file otherwise it
- // it a shader
- func processFile(file string, include bool) {
- // Ignore file if it has not the shader extension
- fext := filepath.Ext(file)
- if fext != SHADEREXT {
- return
- }
- if *oVerbose {
- logger.Printf("Processing: %s", file)
- }
- // Get the file base name and its name with the extension
- fbase := filepath.Base(file)
- fname := fbase[:len(fbase)-len(fext)]
- // If not in include directory, the file must be a shader program
- // which name must have the format: <name>_<shader_type>
- if !include {
- parts := strings.Split(string(fname), "_")
- if len(parts) < 2 {
- logger.Print(" IGNORED: file does not have a valid shader name")
- return
- }
- stype := parts[len(parts)-1]
- if !shaderTypes[stype] {
- logger.Print(" IGNORED: invalid shader type")
- return
- }
- sname := strings.Join(parts[:len(parts)-1], "_")
- pinfo, ok := templData.Programs[sname]
- if !ok {
- templData.Programs[sname] = pinfo
- }
- switch stype {
- case TYPE_VERTEX:
- pinfo.Vertex = fname
- case TYPE_FRAGMENT:
- pinfo.Fragment = fname
- case TYPE_GEOMETRY:
- pinfo.Geometry = fname
- }
- templData.Programs[sname] = pinfo
- }
- // Reads all file data
- f, err := os.Open(file)
- if err != nil {
- panic(err)
- }
- defer f.Close()
- data, err := ioutil.ReadAll(f)
- if err != nil {
- panic(err)
- }
- // Appends entry in Includes or Shaders
- if include {
- templData.Includes = append(templData.Includes, fileInfo{
- Name: fname,
- Source: string(data),
- })
- } else {
- templData.Shaders = append(templData.Shaders, fileInfo{
- Name: fname,
- Source: string(data),
- })
- }
- templData.Count++
- }
- // generate generates output go file with shaders sources from TEMPLATE
- func generate(file string) {
- if *oVerbose {
- logger.Printf("Generating: %s", file)
- }
- // Parses the template
- tmpl := template.New("tmpl")
- tmpl, err := tmpl.Parse(TEMPLATE)
- if err != nil {
- panic(err)
- }
- // Expands template to buffer
- var buf bytes.Buffer
- err = tmpl.Execute(&buf, &templData)
- if err != nil {
- panic(err)
- }
- // Formats buffer as Go source
- p, err := format.Source(buf.Bytes())
- if err != nil {
- panic(err)
- }
- // Writes formatted source to output file
- f, err := os.Create(file)
- if err != nil {
- panic(err)
- }
- f.Write(p)
- f.Close()
- }
- // usage shows the application usage
- func usage() {
- fmt.Fprintf(os.Stderr, "%s v%d.%d\n", PROGNAME, VMAJOR, VMINOR)
- fmt.Fprintf(os.Stderr, "usage: %s [options]\n", strings.ToLower(PROGNAME))
- flag.PrintDefaults()
- os.Exit(2)
- }
|