shaman.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. package renderer
  5. import (
  6. "bytes"
  7. "fmt"
  8. "text/template"
  9. "github.com/g3n/engine/gls"
  10. "github.com/g3n/engine/material"
  11. "github.com/g3n/engine/renderer/shader"
  12. )
  13. type ShaderSpecs struct {
  14. Name string // Shader name
  15. Version string // GLSL version
  16. ShaderUnique bool // indicates if shader is independent of lights and textures
  17. UseLights material.UseLights // Bitmask indicating which lights to consider
  18. AmbientLightsMax int // Current number of ambient lights
  19. DirLightsMax int // Current Number of directional lights
  20. PointLightsMax int // Current Number of point lights
  21. SpotLightsMax int // Current Number of spot lights
  22. MatTexturesMax int // Current Number of material textures
  23. }
  24. type ProgSpecs struct {
  25. program *gls.Program // program object
  26. specs ShaderSpecs // associated specs
  27. }
  28. type Shaman struct {
  29. gs *gls.GLS
  30. chunks *template.Template // template with all chunks
  31. shaders map[string]*template.Template // maps shader name to its template
  32. proginfo map[string]shader.ProgramInfo // maps name of the program to ProgramInfo
  33. programs []ProgSpecs // list of compiled programs with specs
  34. specs ShaderSpecs // Current shader specs
  35. }
  36. // NewShaman creates and returns a pointer to a new shader manager
  37. func NewShaman(gs *gls.GLS) *Shaman {
  38. sm := new(Shaman)
  39. sm.Init(gs)
  40. return sm
  41. }
  42. // Init initializes the shader manager
  43. func (sm *Shaman) Init(gs *gls.GLS) {
  44. sm.gs = gs
  45. sm.chunks = template.New("_chunks_")
  46. sm.shaders = make(map[string]*template.Template)
  47. sm.proginfo = make(map[string]shader.ProgramInfo)
  48. // Add "loop" function to chunks template
  49. // "loop" is used inside the shader templates to unroll loops.
  50. sm.chunks.Funcs(template.FuncMap{
  51. "loop": func(n int) []int {
  52. s := make([]int, n)
  53. for i := range s {
  54. s[i] = i
  55. }
  56. return s
  57. },
  58. })
  59. }
  60. // AddDefaultShaders adds to the shader manager all default
  61. // shaders statically registered.
  62. func (sm *Shaman) AddDefaultShaders() error {
  63. for name, source := range shader.Chunks() {
  64. err := sm.AddChunk(name, source)
  65. if err != nil {
  66. return err
  67. }
  68. }
  69. for name, source := range shader.Shaders() {
  70. err := sm.AddShader(name, source)
  71. if err != nil {
  72. return err
  73. }
  74. }
  75. for name, pinfo := range shader.Programs() {
  76. sm.proginfo[name] = pinfo
  77. }
  78. return nil
  79. }
  80. // AddChunk adds a shader chunk with the specified name and source code
  81. func (sm *Shaman) AddChunk(name, source string) error {
  82. tmpl := sm.chunks.New(name)
  83. _, err := tmpl.Parse(source)
  84. if err != nil {
  85. return err
  86. }
  87. return nil
  88. }
  89. // AddShader adds a shader program with the specified name and source code
  90. func (sm *Shaman) AddShader(name, source string) error {
  91. // Clone chunks template so any shader can use
  92. // any of the chunks
  93. tmpl, err := sm.chunks.Clone()
  94. if err != nil {
  95. return err
  96. }
  97. // Parses this shader template source
  98. _, err = tmpl.Parse(source)
  99. if err != nil {
  100. return err
  101. }
  102. sm.shaders[name] = tmpl
  103. return nil
  104. }
  105. // AddProgram adds a program with the specified name and associated vertex
  106. // and fragment shaders names (previously registered)
  107. // To specify other types of shaders for a program use SetProgramShader()
  108. func (sm *Shaman) AddProgram(name, vertexName, fragName string) error {
  109. sm.proginfo[name] = shader.ProgramInfo{Vertex: vertexName, Frag: fragName}
  110. return nil
  111. }
  112. // SetProgramShader sets the shader type and name for a previously specified program name.
  113. // Returns error if the specified program or shader name not found or
  114. // if an invalid shader type was specified.
  115. func (sm *Shaman) SetProgramShader(pname string, stype int, sname string) error {
  116. // Checks if program name is valid
  117. pinfo, ok := sm.proginfo[pname]
  118. if !ok {
  119. return fmt.Errorf("Program name:%s not found", pname)
  120. }
  121. // Checks if shader name is valid
  122. _, ok = sm.shaders[sname]
  123. if !ok {
  124. return fmt.Errorf("Shader name:%s not found", sname)
  125. }
  126. // Sets the program shader name for the specified type
  127. switch stype {
  128. case gls.VERTEX_SHADER:
  129. pinfo.Vertex = sname
  130. case gls.FRAGMENT_SHADER:
  131. pinfo.Frag = sname
  132. case gls.GEOMETRY_SHADER:
  133. pinfo.Geometry = sname
  134. default:
  135. return fmt.Errorf("Invalid shader type")
  136. }
  137. sm.proginfo[pname] = pinfo
  138. log.Error("SetProgramShader: %v", pinfo)
  139. return nil
  140. }
  141. // SetProgram set the shader program to satisfy the specified specs.
  142. // Returns an indication if the current shader has changed and a possible error
  143. // when creating a new shader program.
  144. // Receives a copy of the specs because it changes the fields which specify the
  145. // number of lights depending on the UseLights flags.
  146. func (sm *Shaman) SetProgram(s *ShaderSpecs) (bool, error) {
  147. // Checks material use lights bit mask
  148. specs := *s
  149. if (specs.UseLights & material.UseLightAmbient) == 0 {
  150. specs.AmbientLightsMax = 0
  151. }
  152. if (specs.UseLights & material.UseLightDirectional) == 0 {
  153. specs.DirLightsMax = 0
  154. }
  155. if (specs.UseLights & material.UseLightPoint) == 0 {
  156. specs.PointLightsMax = 0
  157. }
  158. if (specs.UseLights & material.UseLightSpot) == 0 {
  159. specs.SpotLightsMax = 0
  160. }
  161. // If current shader specs are the same as the specified specs, nothing to do.
  162. if sm.specs.Compare(&specs) {
  163. return false, nil
  164. }
  165. // Search for compiled program with the specified specs
  166. for _, pinfo := range sm.programs {
  167. if pinfo.specs.Compare(&specs) {
  168. sm.gs.UseProgram(pinfo.program)
  169. sm.specs = specs
  170. return true, nil
  171. }
  172. }
  173. // Generates new program with the specified specs
  174. prog, err := sm.GenProgram(&specs)
  175. if err != nil {
  176. return false, err
  177. }
  178. log.Debug("Created new shader:%v", specs.Name)
  179. // Save specs as current specs, adds new program to the list
  180. // and actives program
  181. sm.specs = specs
  182. sm.programs = append(sm.programs, ProgSpecs{prog, specs})
  183. sm.gs.UseProgram(prog)
  184. return true, nil
  185. }
  186. // Generates shader program from the specified specs
  187. func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
  188. // Get info for the specified shader program
  189. progInfo, ok := sm.proginfo[specs.Name]
  190. if !ok {
  191. return nil, fmt.Errorf("Program:%s not found", specs.Name)
  192. }
  193. // Sets the GLSL version string
  194. specs.Version = "330 core"
  195. // Get vertex shader compiled template
  196. vtempl, ok := sm.shaders[progInfo.Vertex]
  197. if !ok {
  198. return nil, fmt.Errorf("Vertex shader:%s template not found", progInfo.Vertex)
  199. }
  200. // Generates vertex shader source from template
  201. var sourceVertex bytes.Buffer
  202. err := vtempl.Execute(&sourceVertex, specs)
  203. if err != nil {
  204. return nil, err
  205. }
  206. // Get fragment shader compiled template
  207. fragTempl, ok := sm.shaders[progInfo.Frag]
  208. if !ok {
  209. return nil, fmt.Errorf("Fragment shader:%s template not found", progInfo.Frag)
  210. }
  211. // Generates fragment shader source from template
  212. var sourceFrag bytes.Buffer
  213. err = fragTempl.Execute(&sourceFrag, specs)
  214. if err != nil {
  215. return nil, err
  216. }
  217. log.Error("Shader:%s INFO:%+v", specs.Name, progInfo)
  218. // Checks for optional geometry shader compiled template
  219. var sourceGeom bytes.Buffer
  220. if progInfo.Geometry != "" {
  221. // Get geometry shader compiled template
  222. geomTempl, ok := sm.shaders[progInfo.Geometry]
  223. if !ok {
  224. return nil, fmt.Errorf("Geometry shader:%s template not found", progInfo.Geometry)
  225. }
  226. // Generates geometry shader source from template
  227. err = geomTempl.Execute(&sourceGeom, specs)
  228. if err != nil {
  229. return nil, err
  230. }
  231. }
  232. // Creates shader program
  233. prog := sm.gs.NewProgram()
  234. prog.AddShader(gls.VERTEX_SHADER, sourceVertex.String(), nil)
  235. prog.AddShader(gls.FRAGMENT_SHADER, sourceFrag.String(), nil)
  236. if progInfo.Geometry != "" {
  237. prog.AddShader(gls.GEOMETRY_SHADER, sourceGeom.String(), nil)
  238. }
  239. err = prog.Build()
  240. if err != nil {
  241. return nil, err
  242. }
  243. return prog, nil
  244. }
  245. // Compare compares two shaders specifications structures
  246. func (ss *ShaderSpecs) Compare(other *ShaderSpecs) bool {
  247. if ss.Name != other.Name {
  248. return false
  249. }
  250. if other.ShaderUnique {
  251. return true
  252. }
  253. if ss.AmbientLightsMax == other.AmbientLightsMax &&
  254. ss.DirLightsMax == other.DirLightsMax &&
  255. ss.PointLightsMax == other.PointLightsMax &&
  256. ss.SpotLightsMax == other.SpotLightsMax &&
  257. ss.MatTexturesMax == other.MatTexturesMax {
  258. return true
  259. }
  260. return false
  261. }