main.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // g3nshaders reads shaders files with ".glsl" extensions and generates
  5. // a Go file containing strings with the content of these files.
  6. // Also it builds maps associating include and shader names to its respective
  7. // source strings.
  8. // Usage:
  9. // g3nshaders -in=<input_dir> -out<output_gofile> -v
  10. // It is normally invoked by "go generate" inside the "shaders" directory
  11. package main
  12. import (
  13. "bytes"
  14. "flag"
  15. "fmt"
  16. "go/format"
  17. "io/ioutil"
  18. "log"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "text/template"
  23. )
  24. // Program constants.
  25. const (
  26. PROGNAME = "g3nshaders"
  27. VMAJOR = 0
  28. VMINOR = 1
  29. SHADEREXT = ".glsl"
  30. DIR_INCLUDE = "include"
  31. TYPE_VERTEX = "vertex"
  32. TYPE_FRAGMENT = "fragment"
  33. TYPE_GEOMETRY = "geometry"
  34. )
  35. //
  36. // TEMPLATE is a Go template to generate the output file with the shaders' sources and
  37. // maps describing the include and shader names and programs shaders.
  38. //
  39. const TEMPLATE = `// File generated by G3NSHADERS. Do not edit.
  40. // To regenerate this file install 'g3nshaders' and execute:
  41. // 'go generate' in this folder.
  42. package {{.Pkg}}
  43. {{range .Includes}}
  44. const include_{{.Name}}_source = ` + "`{{.Source}}`" + `
  45. {{end}}
  46. {{range .Shaders}}
  47. const {{.Name}}_source = ` + "`{{.Source}}`" + `
  48. {{end}}
  49. // Maps include name with its source code
  50. var includeMap = map[string]string {
  51. {{range .Includes}}
  52. "{{- .Name}}": include_{{.Name}}_source, {{end}}
  53. }
  54. // Maps shader name with its source code
  55. var shaderMap = map[string]string {
  56. {{range .Shaders}}
  57. "{{- .Name}}": {{.Name}}_source, {{end}}
  58. }
  59. // Maps program name with Proginfo struct with shaders names
  60. var programMap = map[string]ProgramInfo{
  61. {{ range $progName, $progInfo := .Programs }}
  62. "{{$progName}}": { "{{$progInfo.Vertex}}","{{$progInfo.Fragment}}","{{$progInfo.Geometry}}" }, {{end}}
  63. }
  64. `
  65. // Command line options
  66. var (
  67. oVersion = flag.Bool("version", false, "Show version and exits")
  68. oInp = flag.String("in", ".", "Input directory")
  69. oOut = flag.String("out", "sources.go", "Go output file")
  70. oPackage = flag.String("pkg", "shaders", "Package name")
  71. oVerbose = flag.Bool("v", false, "Show files being processed")
  72. )
  73. // Valid shader types
  74. var shaderTypes = map[string]bool{
  75. TYPE_VERTEX: true,
  76. TYPE_FRAGMENT: true,
  77. TYPE_GEOMETRY: true,
  78. }
  79. // fileInfo describes a shader or include file name and source code
  80. type fileInfo struct {
  81. Name string // shader or include name
  82. Source string // shader or include source code
  83. Include bool // true if include, false otherwise
  84. }
  85. // progInfo describes all the shader names of an specific program
  86. // If the program doesn't use the geometry shader it is set as an empty string
  87. type progInfo struct {
  88. Vertex string // vertex shader name
  89. Fragment string // fragment shader name
  90. Geometry string // geometry shader name
  91. }
  92. // templInfo contains all information needed for the template expansion
  93. type templInfo struct {
  94. Count int // number of shader files processed
  95. Pkg string // name of the package for the generated output file
  96. Includes []fileInfo // list of include files found
  97. Shaders []fileInfo // list of shader files found
  98. Programs map[string]progInfo // map of shader programs found
  99. }
  100. var templData templInfo // global template data
  101. var logger *log.Logger // global logger
  102. func main() {
  103. // Parse command line parameters
  104. flag.Usage = usage
  105. flag.Parse()
  106. // If requested, print version and exits
  107. if *oVersion == true {
  108. fmt.Fprintf(os.Stderr, "%s v%d.%d\n", PROGNAME, VMAJOR, VMINOR)
  109. return
  110. }
  111. // Creates logger
  112. logger = log.New(os.Stdout, "G3NSHADERS ", log.Ldate|log.Ltime)
  113. // Initialize template data
  114. templData.Pkg = *oPackage
  115. templData.Programs = make(map[string]progInfo)
  116. // Process the current directory and its subdirectories recursively
  117. // appending information into templData
  118. processDir(*oInp, false)
  119. if templData.Count == 0 {
  120. log.Print("No shader files found")
  121. return
  122. }
  123. // Generates output file from TEMPLATE
  124. generate(*oOut)
  125. }
  126. // processDir processes recursively all shaders files in the specified directory
  127. func processDir(dir string, include bool) {
  128. // Open directory to process
  129. f, err := os.Open(dir)
  130. if err != nil {
  131. panic(err)
  132. }
  133. defer f.Close()
  134. // Read all file entries from the directory
  135. finfos, err := f.Readdir(0)
  136. if err != nil {
  137. panic(err)
  138. }
  139. // Process all directory entries.
  140. for _, fi := range finfos {
  141. if fi.IsDir() {
  142. dirInclude := include
  143. if fi.Name() == DIR_INCLUDE {
  144. dirInclude = true
  145. }
  146. processDir(filepath.Join(dir, fi.Name()), dirInclude)
  147. } else {
  148. processFile(filepath.Join(dir, fi.Name()), include)
  149. }
  150. }
  151. }
  152. // processFile process one file checking if it has the shaders extension,
  153. // otherwise it is ignored.
  154. // If the include flag is true the file is an include file otherwise it
  155. // it a shader
  156. func processFile(file string, include bool) {
  157. // Ignore file if it has not the shader extension
  158. fext := filepath.Ext(file)
  159. if fext != SHADEREXT {
  160. return
  161. }
  162. if *oVerbose {
  163. logger.Printf("Processing: %s", file)
  164. }
  165. // Get the file base name and its name with the extension
  166. fbase := filepath.Base(file)
  167. fname := fbase[:len(fbase)-len(fext)]
  168. // If not in include directory, the file must be a shader program
  169. // which name must have the format: <name>_<shader_type>
  170. if !include {
  171. parts := strings.Split(string(fname), "_")
  172. if len(parts) < 2 {
  173. logger.Print(" IGNORED: file does not have a valid shader name")
  174. return
  175. }
  176. stype := parts[len(parts)-1]
  177. if !shaderTypes[stype] {
  178. logger.Print(" IGNORED: invalid shader type")
  179. return
  180. }
  181. sname := strings.Join(parts[:len(parts)-1], "_")
  182. pinfo, ok := templData.Programs[sname]
  183. if !ok {
  184. templData.Programs[sname] = pinfo
  185. }
  186. switch stype {
  187. case TYPE_VERTEX:
  188. pinfo.Vertex = fname
  189. case TYPE_FRAGMENT:
  190. pinfo.Fragment = fname
  191. case TYPE_GEOMETRY:
  192. pinfo.Geometry = fname
  193. }
  194. templData.Programs[sname] = pinfo
  195. }
  196. // Reads all file data
  197. f, err := os.Open(file)
  198. if err != nil {
  199. panic(err)
  200. }
  201. defer f.Close()
  202. data, err := ioutil.ReadAll(f)
  203. if err != nil {
  204. panic(err)
  205. }
  206. // Appends entry in Includes or Shaders
  207. if include {
  208. templData.Includes = append(templData.Includes, fileInfo{
  209. Name: fname,
  210. Source: string(data),
  211. })
  212. } else {
  213. templData.Shaders = append(templData.Shaders, fileInfo{
  214. Name: fname,
  215. Source: string(data),
  216. })
  217. }
  218. templData.Count++
  219. }
  220. // generate generates output go file with shaders sources from TEMPLATE
  221. func generate(file string) {
  222. if *oVerbose {
  223. logger.Printf("Generating: %s", file)
  224. }
  225. // Parses the template
  226. tmpl := template.New("tmpl")
  227. tmpl, err := tmpl.Parse(TEMPLATE)
  228. if err != nil {
  229. panic(err)
  230. }
  231. // Expands template to buffer
  232. var buf bytes.Buffer
  233. err = tmpl.Execute(&buf, &templData)
  234. if err != nil {
  235. panic(err)
  236. }
  237. // Formats buffer as Go source
  238. p, err := format.Source(buf.Bytes())
  239. if err != nil {
  240. panic(err)
  241. }
  242. // Writes formatted source to output file
  243. f, err := os.Create(file)
  244. if err != nil {
  245. panic(err)
  246. }
  247. f.Write(p)
  248. f.Close()
  249. }
  250. // usage shows the application usage
  251. func usage() {
  252. fmt.Fprintf(os.Stderr, "%s v%d.%d\n", PROGNAME, VMAJOR, VMINOR)
  253. fmt.Fprintf(os.Stderr, "usage: %s [options]\n", strings.ToLower(PROGNAME))
  254. flag.PrintDefaults()
  255. os.Exit(2)
  256. }