shaman.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. "fmt"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "github.com/g3n/engine/gls"
  11. "github.com/g3n/engine/material"
  12. "github.com/g3n/engine/renderer/shaders"
  13. )
  14. // Regular expression to parse #include <name> directive
  15. var rexInclude *regexp.Regexp
  16. func init() {
  17. rexInclude = regexp.MustCompile(`#include\s+<(.*)>`)
  18. }
  19. // ShaderSpecs describes the specification of a compiled shader program
  20. type ShaderSpecs struct {
  21. Name string // Shader name
  22. Version string // GLSL version
  23. ShaderUnique bool // indicates if shader is independent of lights and textures
  24. UseLights material.UseLights // Bitmask indicating which lights to consider
  25. AmbientLightsMax int // Current number of ambient lights
  26. DirLightsMax int // Current Number of directional lights
  27. PointLightsMax int // Current Number of point lights
  28. SpotLightsMax int // Current Number of spot lights
  29. MatTexturesMax int // Current Number of material textures
  30. Defines map[string]string // Additional shader defines
  31. }
  32. type ProgSpecs struct {
  33. program *gls.Program // program object
  34. specs ShaderSpecs // associated specs
  35. }
  36. type Shaman struct {
  37. gs *gls.GLS
  38. includes map[string]string // include files sources
  39. shadersm map[string]string // maps shader name to its template
  40. proginfo map[string]shaders.ProgramInfo // maps name of the program to ProgramInfo
  41. programs []ProgSpecs // list of compiled programs with specs
  42. specs ShaderSpecs // Current shader specs
  43. }
  44. // NewShaman creates and returns a pointer to a new shader manager
  45. func NewShaman(gs *gls.GLS) *Shaman {
  46. sm := new(Shaman)
  47. sm.Init(gs)
  48. return sm
  49. }
  50. // Init initializes the shader manager
  51. func (sm *Shaman) Init(gs *gls.GLS) {
  52. sm.gs = gs
  53. sm.includes = make(map[string]string)
  54. sm.shadersm = make(map[string]string)
  55. sm.proginfo = make(map[string]shaders.ProgramInfo)
  56. }
  57. // AddDefaultShaders adds to this shader manager all default
  58. // include chunks, shaders and programs statically registered.
  59. func (sm *Shaman) AddDefaultShaders() error {
  60. for _, name := range shaders.Includes() {
  61. sm.AddChunk(name, shaders.IncludeSource(name))
  62. }
  63. for _, name := range shaders.Shaders() {
  64. sm.AddShader(name, shaders.ShaderSource(name))
  65. }
  66. for _, name := range shaders.Programs() {
  67. sm.proginfo[name] = shaders.GetProgramInfo(name)
  68. }
  69. return nil
  70. }
  71. // AddChunk adds a shader chunk with the specified name and source code
  72. func (sm *Shaman) AddChunk(name, source string) {
  73. sm.includes[name] = source
  74. }
  75. // AddShader adds a shader program with the specified name and source code
  76. func (sm *Shaman) AddShader(name, source string) {
  77. sm.shadersm[name] = source
  78. }
  79. // AddProgram adds a program with the specified name and associated vertex
  80. // and fragment shaders names (previously registered)
  81. func (sm *Shaman) AddProgram(name, vertexName, fragName string, others ...string) {
  82. geomName := ""
  83. if len(others) > 0 {
  84. geomName = others[0]
  85. }
  86. sm.proginfo[name] = shaders.ProgramInfo{
  87. Vertex: vertexName,
  88. Fragment: fragName,
  89. Geometry: geomName,
  90. }
  91. }
  92. // SetProgram set the shader program to satisfy the specified specs.
  93. // Returns an indication if the current shader has changed and a possible error
  94. // when creating a new shader program.
  95. // Receives a copy of the specs because it changes the fields which specify the
  96. // number of lights depending on the UseLights flags.
  97. func (sm *Shaman) SetProgram(s *ShaderSpecs) (bool, error) {
  98. // Checks material use lights bit mask
  99. var specs ShaderSpecs
  100. specs.copy(s)
  101. if (specs.UseLights & material.UseLightAmbient) == 0 {
  102. specs.AmbientLightsMax = 0
  103. }
  104. if (specs.UseLights & material.UseLightDirectional) == 0 {
  105. specs.DirLightsMax = 0
  106. }
  107. if (specs.UseLights & material.UseLightPoint) == 0 {
  108. specs.PointLightsMax = 0
  109. }
  110. if (specs.UseLights & material.UseLightSpot) == 0 {
  111. specs.SpotLightsMax = 0
  112. }
  113. // If current shader specs are the same as the specified specs, nothing to do.
  114. if sm.specs.compare(&specs) {
  115. return false, nil
  116. }
  117. // Search for compiled program with the specified specs
  118. for _, pinfo := range sm.programs {
  119. if pinfo.specs.compare(&specs) {
  120. sm.gs.UseProgram(pinfo.program)
  121. sm.specs = specs
  122. return true, nil
  123. }
  124. }
  125. // Generates new program with the specified specs
  126. prog, err := sm.GenProgram(&specs)
  127. if err != nil {
  128. return false, err
  129. }
  130. log.Debug("Created new shader:%v", specs.Name)
  131. // Save specs as current specs, adds new program to the list and activates the program
  132. sm.specs = specs
  133. sm.programs = append(sm.programs, ProgSpecs{prog, specs})
  134. sm.gs.UseProgram(prog)
  135. return true, nil
  136. }
  137. // Generates shader program from the specified specs
  138. func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
  139. // Get info for the specified shader program
  140. progInfo, ok := sm.proginfo[specs.Name]
  141. if !ok {
  142. return nil, fmt.Errorf("Program:%s not found", specs.Name)
  143. }
  144. // Sets the defines map
  145. defines := map[string]string{}
  146. defines["GLSL_VERSION"] = "330 core"
  147. defines["AMB_LIGHTS"] = strconv.FormatUint(uint64(specs.AmbientLightsMax), 10)
  148. defines["DIR_LIGHTS"] = strconv.FormatUint(uint64(specs.DirLightsMax), 10)
  149. defines["POINT_LIGHTS"] = strconv.FormatUint(uint64(specs.PointLightsMax), 10)
  150. defines["SPOT_LIGHTS"] = strconv.FormatUint(uint64(specs.SpotLightsMax), 10)
  151. defines["MAT_TEXTURES"] = strconv.FormatUint(uint64(specs.MatTexturesMax), 10)
  152. // Adds additional material defines from the specs parameter
  153. for name, value := range specs.Defines {
  154. defines[name] = value
  155. }
  156. // Get vertex shader source
  157. vertexSource, ok := sm.shadersm[progInfo.Vertex]
  158. if !ok {
  159. return nil, fmt.Errorf("Vertex shader:%s not found", progInfo.Vertex)
  160. }
  161. // Preprocess vertex shader source
  162. vertexSource, err := sm.preprocess(vertexSource, defines)
  163. if err != nil {
  164. return nil, err
  165. }
  166. //fmt.Printf("vertexSource:%s\n", vertexSource)
  167. // Get fragment shader source
  168. fragSource, ok := sm.shadersm[progInfo.Fragment]
  169. if err != nil {
  170. return nil, fmt.Errorf("Fragment shader:%s not found", progInfo.Fragment)
  171. }
  172. // Preprocess fragment shader source
  173. fragSource, err = sm.preprocess(fragSource, defines)
  174. if err != nil {
  175. return nil, err
  176. }
  177. //fmt.Printf("fragSource:%s\n", fragSource)
  178. // Checks for optional geometry shader compiled template
  179. var geomSource = ""
  180. if progInfo.Geometry != "" {
  181. // Get geometry shader source
  182. geomSource, ok = sm.shadersm[progInfo.Geometry]
  183. if !ok {
  184. return nil, fmt.Errorf("Geometry shader:%s not found", progInfo.Geometry)
  185. }
  186. // Preprocess geometry shader source
  187. geomSource, err = sm.preprocess(geomSource, defines)
  188. if err != nil {
  189. return nil, err
  190. }
  191. }
  192. // Creates shader program
  193. prog := sm.gs.NewProgram()
  194. prog.AddShader(gls.VERTEX_SHADER, vertexSource, nil)
  195. prog.AddShader(gls.FRAGMENT_SHADER, fragSource, nil)
  196. if progInfo.Geometry != "" {
  197. prog.AddShader(gls.GEOMETRY_SHADER, geomSource, nil)
  198. }
  199. err = prog.Build()
  200. if err != nil {
  201. return nil, err
  202. }
  203. return prog, nil
  204. }
  205. // preprocess preprocesses the specified source prefixing it with optional defines directives
  206. // contained in "defines" parameter and replaces '#include <name>' directives
  207. // by the respective source code of include chunk of the specified name.
  208. // The included "files" are also processed recursively.
  209. func (sm *Shaman) preprocess(source string, defines map[string]string) (string, error) {
  210. // If defines map supplied, generates prefix with glsl version directive first,
  211. // followed by "#define directives"
  212. var prefix = ""
  213. if defines != nil {
  214. prefix = fmt.Sprintf("#version %s\n", defines["GLSL_VERSION"])
  215. for name, value := range defines {
  216. if name == "GLSL_VERSION" {
  217. continue
  218. }
  219. prefix = prefix + fmt.Sprintf("#define %s %s\n", name, value)
  220. }
  221. }
  222. // Find all string submatches for the "#include <name>" directive
  223. matches := rexInclude.FindAllStringSubmatch(source, 100)
  224. if len(matches) == 0 {
  225. return prefix + source, nil
  226. }
  227. // For each directive found, replace the name by the respective include chunk source code
  228. var newSource = source
  229. for _, m := range matches {
  230. // Get the source of the include chunk with the match <name>
  231. incSource := sm.includes[m[1]]
  232. if len(incSource) == 0 {
  233. return "", fmt.Errorf("Include:[%s] not found", m[1])
  234. }
  235. // Preprocess the include chunk source code
  236. incSource, err := sm.preprocess(incSource, nil)
  237. if err != nil {
  238. return "", err
  239. }
  240. // Replace all occurances of the include directive with its processed source code
  241. newSource = strings.Replace(newSource, m[0], incSource, -1)
  242. }
  243. return prefix + newSource, nil
  244. }
  245. // copy copies other spec into this
  246. func (ss *ShaderSpecs) copy(other *ShaderSpecs) {
  247. *ss = *other
  248. if other.Defines != nil {
  249. ss.Defines = make(map[string]string)
  250. for k, v := range other.Defines {
  251. ss.Defines[k] = v
  252. }
  253. }
  254. }
  255. // Compare compares two shaders specifications structures
  256. func (ss *ShaderSpecs) compare(other *ShaderSpecs) bool {
  257. if ss.Name != other.Name {
  258. return false
  259. }
  260. if other.ShaderUnique {
  261. return true
  262. }
  263. if ss.AmbientLightsMax == other.AmbientLightsMax &&
  264. ss.DirLightsMax == other.DirLightsMax &&
  265. ss.PointLightsMax == other.PointLightsMax &&
  266. ss.SpotLightsMax == other.SpotLightsMax &&
  267. ss.MatTexturesMax == other.MatTexturesMax &&
  268. ss.compareDefines(other) {
  269. return true
  270. }
  271. return false
  272. }
  273. // compareDefines compares two shaders specification define maps.
  274. func (ss *ShaderSpecs) compareDefines(other *ShaderSpecs) bool {
  275. if ss.Defines == nil && other.Defines == nil {
  276. return true
  277. }
  278. if ss.Defines != nil && other.Defines != nil {
  279. if len(ss.Defines) != len(other.Defines) {
  280. return false
  281. }
  282. for k, _ := range ss.Defines {
  283. v1, ok1 := ss.Defines[k]
  284. v2, ok2 := other.Defines[k]
  285. if v1 != v2 || ok1 != ok2 {
  286. return false
  287. }
  288. }
  289. return true
  290. }
  291. // One is nil and the other is not nil
  292. return false
  293. }