shaman.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. UseLights material.UseLights // Bitmask indicating which lights to consider
  17. AmbientLightsMax int // Current number of ambient lights
  18. DirLightsMax int // Current Number of directional lights
  19. PointLightsMax int // Current Number of point lights
  20. SpotLightsMax int // Current Number of spot lights
  21. MatTexturesMax int // Current Number of material textures
  22. }
  23. type ProgSpecs struct {
  24. program *gls.Program // program object
  25. specs ShaderSpecs // associated specs
  26. }
  27. type Shaman struct {
  28. gs *gls.GLS
  29. chunks *template.Template // template with all chunks
  30. shaders map[string]*template.Template // maps shader name to its template
  31. proginfo map[string]shader.ProgramInfo // maps name of the program to ProgramInfo
  32. programs []ProgSpecs // list of compiled programs with specs
  33. specs ShaderSpecs // Current shader specs
  34. }
  35. // NewShaman creates and returns a pointer to a new shader manager
  36. func NewShaman(gs *gls.GLS) *Shaman {
  37. sm := new(Shaman)
  38. sm.Init(gs)
  39. return sm
  40. }
  41. // Init initializes the shander manager
  42. func (sm *Shaman) Init(gs *gls.GLS) {
  43. sm.gs = gs
  44. sm.chunks = template.New("_chunks_")
  45. sm.shaders = make(map[string]*template.Template)
  46. sm.proginfo = make(map[string]shader.ProgramInfo)
  47. // Add "loop" function to chunks template
  48. // "loop" is used inside the shader templates to unroll loops.
  49. sm.chunks.Funcs(template.FuncMap{
  50. "loop": func(n int) []int {
  51. s := make([]int, n)
  52. for i := range s {
  53. s[i] = i
  54. }
  55. return s
  56. },
  57. })
  58. }
  59. func (sm *Shaman) AddDefaultShaders() error {
  60. for name, source := range shader.Chunks() {
  61. err := sm.AddChunk(name, source)
  62. if err != nil {
  63. return err
  64. }
  65. }
  66. for name, source := range shader.Shaders() {
  67. err := sm.AddShader(name, source)
  68. if err != nil {
  69. return err
  70. }
  71. }
  72. for name, pinfo := range shader.Programs() {
  73. sm.proginfo[name] = pinfo
  74. }
  75. return nil
  76. }
  77. func (sm *Shaman) AddChunk(name, source string) error {
  78. tmpl := sm.chunks.New(name)
  79. _, err := tmpl.Parse(source)
  80. if err != nil {
  81. return err
  82. }
  83. return nil
  84. }
  85. func (sm *Shaman) AddShader(name, source string) error {
  86. // Clone chunks template so any shader can use
  87. // any of the chunks
  88. tmpl, err := sm.chunks.Clone()
  89. if err != nil {
  90. return err
  91. }
  92. // Parses this shader template source
  93. _, err = tmpl.Parse(source)
  94. if err != nil {
  95. return err
  96. }
  97. sm.shaders[name] = tmpl
  98. return nil
  99. }
  100. func (sm *Shaman) AddProgram(name, vertexName, fragName string) error {
  101. sm.proginfo[name] = shader.ProgramInfo{vertexName, fragName}
  102. return nil
  103. }
  104. // SetProgram set the shader program to satisfy the specified specs.
  105. // Returns an indication if the current shader has changed and a possible error
  106. // when creating a new shader program.
  107. // Receives a copy of the specs because it changes the fields which specify the
  108. // number of lights depending on the UseLights flags.
  109. func (sm *Shaman) SetProgram(s *ShaderSpecs) (bool, error) {
  110. // Checks material use lights bit mask
  111. specs := *s
  112. if (specs.UseLights & material.UseLightAmbient) == 0 {
  113. specs.AmbientLightsMax = 0
  114. }
  115. if (specs.UseLights & material.UseLightDirectional) == 0 {
  116. specs.DirLightsMax = 0
  117. }
  118. if (specs.UseLights & material.UseLightPoint) == 0 {
  119. specs.PointLightsMax = 0
  120. }
  121. if (specs.UseLights & material.UseLightSpot) == 0 {
  122. specs.SpotLightsMax = 0
  123. }
  124. // If current shader specs are the same as the specified specs, nothing to do.
  125. if sm.specs.Compare(&specs) {
  126. return false, nil
  127. }
  128. // Search for compiled program with the specified specs
  129. for _, pinfo := range sm.programs {
  130. if pinfo.specs.Compare(&specs) {
  131. sm.gs.UseProgram(pinfo.program)
  132. sm.specs = specs
  133. return true, nil
  134. }
  135. }
  136. // Generates new program with the specified specs
  137. prog, err := sm.GenProgram(&specs)
  138. if err != nil {
  139. return false, err
  140. }
  141. log.Debug("Created new shader:%v", specs.Name)
  142. // Save specs as current specs, adds new program to the list
  143. // and actives program
  144. sm.specs = specs
  145. sm.programs = append(sm.programs, ProgSpecs{prog, specs})
  146. sm.gs.UseProgram(prog)
  147. return true, nil
  148. }
  149. // Generates shader program from the specified specs
  150. func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
  151. // Get info for the specified shader program
  152. progInfo, ok := sm.proginfo[specs.Name]
  153. if !ok {
  154. return nil, fmt.Errorf("Program:%s not found", specs.Name)
  155. }
  156. // Sets the GLSL version string
  157. specs.Version = "330 core"
  158. // Get vertex shader compiled template
  159. vtempl, ok := sm.shaders[progInfo.Vertex]
  160. if !ok {
  161. return nil, fmt.Errorf("Shader:%s template not found", progInfo.Vertex)
  162. }
  163. // Generates vertex shader source from template
  164. var sourceVertex bytes.Buffer
  165. err := vtempl.Execute(&sourceVertex, specs)
  166. if err != nil {
  167. return nil, err
  168. }
  169. // Get fragment shader compiled template
  170. fragTempl, ok := sm.shaders[progInfo.Frag]
  171. if !ok {
  172. return nil, fmt.Errorf("Shader:%s template not found", progInfo.Frag)
  173. }
  174. // Generates fragment shader source from template
  175. var sourceFrag bytes.Buffer
  176. err = fragTempl.Execute(&sourceFrag, specs)
  177. if err != nil {
  178. return nil, err
  179. }
  180. // Creates shader program
  181. prog := sm.gs.NewProgram()
  182. prog.AddShader(gls.VERTEX_SHADER, sourceVertex.String(), nil)
  183. prog.AddShader(gls.FRAGMENT_SHADER, sourceFrag.String(), nil)
  184. err = prog.Build()
  185. if err != nil {
  186. return nil, err
  187. }
  188. return prog, nil
  189. }
  190. func (ss *ShaderSpecs) Compare(other *ShaderSpecs) bool {
  191. if ss.Name == other.Name &&
  192. ss.AmbientLightsMax == other.AmbientLightsMax &&
  193. ss.DirLightsMax == other.DirLightsMax &&
  194. ss.PointLightsMax == other.PointLightsMax &&
  195. ss.SpotLightsMax == other.SpotLightsMax &&
  196. ss.MatTexturesMax == other.MatTexturesMax {
  197. return true
  198. }
  199. return false
  200. }