main.go 7.2 KB

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