shaman.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 shander 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. func (sm *Shaman) AddDefaultShaders() error {
  61. for name, source := range shader.Chunks() {
  62. err := sm.AddChunk(name, source)
  63. if err != nil {
  64. return err
  65. }
  66. }
  67. for name, source := range shader.Shaders() {
  68. err := sm.AddShader(name, source)
  69. if err != nil {
  70. return err
  71. }
  72. }
  73. for name, pinfo := range shader.Programs() {
  74. sm.proginfo[name] = pinfo
  75. }
  76. return nil
  77. }
  78. func (sm *Shaman) AddChunk(name, source string) error {
  79. tmpl := sm.chunks.New(name)
  80. _, err := tmpl.Parse(source)
  81. if err != nil {
  82. return err
  83. }
  84. return nil
  85. }
  86. func (sm *Shaman) AddShader(name, source string) error {
  87. // Clone chunks template so any shader can use
  88. // any of the chunks
  89. tmpl, err := sm.chunks.Clone()
  90. if err != nil {
  91. return err
  92. }
  93. // Parses this shader template source
  94. _, err = tmpl.Parse(source)
  95. if err != nil {
  96. return err
  97. }
  98. sm.shaders[name] = tmpl
  99. return nil
  100. }
  101. func (sm *Shaman) AddProgram(name, vertexName, fragName string) error {
  102. sm.proginfo[name] = shader.ProgramInfo{vertexName, fragName}
  103. return nil
  104. }
  105. // SetProgram set the shader program to satisfy the specified specs.
  106. // Returns an indication if the current shader has changed and a possible error
  107. // when creating a new shader program.
  108. // Receives a copy of the specs because it changes the fields which specify the
  109. // number of lights depending on the UseLights flags.
  110. func (sm *Shaman) SetProgram(s *ShaderSpecs) (bool, error) {
  111. // Checks material use lights bit mask
  112. specs := *s
  113. if (specs.UseLights & material.UseLightAmbient) == 0 {
  114. specs.AmbientLightsMax = 0
  115. }
  116. if (specs.UseLights & material.UseLightDirectional) == 0 {
  117. specs.DirLightsMax = 0
  118. }
  119. if (specs.UseLights & material.UseLightPoint) == 0 {
  120. specs.PointLightsMax = 0
  121. }
  122. if (specs.UseLights & material.UseLightSpot) == 0 {
  123. specs.SpotLightsMax = 0
  124. }
  125. // If current shader specs are the same as the specified specs, nothing to do.
  126. if sm.specs.Compare(&specs) {
  127. return false, nil
  128. }
  129. // Search for compiled program with the specified specs
  130. for _, pinfo := range sm.programs {
  131. if pinfo.specs.Compare(&specs) {
  132. sm.gs.UseProgram(pinfo.program)
  133. sm.specs = specs
  134. return true, nil
  135. }
  136. }
  137. // Generates new program with the specified specs
  138. prog, err := sm.GenProgram(&specs)
  139. if err != nil {
  140. return false, err
  141. }
  142. log.Debug("Created new shader:%v", specs.Name)
  143. // Save specs as current specs, adds new program to the list
  144. // and actives program
  145. sm.specs = specs
  146. sm.programs = append(sm.programs, ProgSpecs{prog, specs})
  147. sm.gs.UseProgram(prog)
  148. return true, nil
  149. }
  150. // Generates shader program from the specified specs
  151. func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
  152. // Get info for the specified shader program
  153. progInfo, ok := sm.proginfo[specs.Name]
  154. if !ok {
  155. return nil, fmt.Errorf("Program:%s not found", specs.Name)
  156. }
  157. // Sets the GLSL version string
  158. specs.Version = "330 core"
  159. // Get vertex shader compiled template
  160. vtempl, ok := sm.shaders[progInfo.Vertex]
  161. if !ok {
  162. return nil, fmt.Errorf("Shader:%s template not found", progInfo.Vertex)
  163. }
  164. // Generates vertex shader source from template
  165. var sourceVertex bytes.Buffer
  166. err := vtempl.Execute(&sourceVertex, specs)
  167. if err != nil {
  168. return nil, err
  169. }
  170. // Get fragment shader compiled template
  171. fragTempl, ok := sm.shaders[progInfo.Frag]
  172. if !ok {
  173. return nil, fmt.Errorf("Shader:%s template not found", progInfo.Frag)
  174. }
  175. // Generates fragment shader source from template
  176. var sourceFrag bytes.Buffer
  177. err = fragTempl.Execute(&sourceFrag, specs)
  178. if err != nil {
  179. return nil, err
  180. }
  181. // Creates shader program
  182. prog := sm.gs.NewProgram()
  183. prog.AddShader(gls.VERTEX_SHADER, sourceVertex.String(), nil)
  184. prog.AddShader(gls.FRAGMENT_SHADER, sourceFrag.String(), nil)
  185. err = prog.Build()
  186. if err != nil {
  187. return nil, err
  188. }
  189. return prog, nil
  190. }
  191. // Compare compares two shaders specifications structures
  192. func (ss *ShaderSpecs) Compare(other *ShaderSpecs) bool {
  193. if ss.Name != other.Name {
  194. return false
  195. }
  196. if other.ShaderUnique {
  197. return true
  198. }
  199. if ss.AmbientLightsMax == other.AmbientLightsMax &&
  200. ss.DirLightsMax == other.DirLightsMax &&
  201. ss.PointLightsMax == other.PointLightsMax &&
  202. ss.SpotLightsMax == other.SpotLightsMax &&
  203. ss.MatTexturesMax == other.MatTexturesMax {
  204. return true
  205. }
  206. return false
  207. }