renderer.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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. "github.com/g3n/engine/camera"
  7. "github.com/g3n/engine/core"
  8. "github.com/g3n/engine/gls"
  9. "github.com/g3n/engine/graphic"
  10. "github.com/g3n/engine/gui"
  11. "github.com/g3n/engine/light"
  12. "github.com/g3n/engine/material"
  13. "github.com/g3n/engine/math32"
  14. "sort"
  15. )
  16. // Renderer renders a scene containing 3D objects and/or 2D GUI elements.
  17. type Renderer struct {
  18. Shaman // Embedded shader manager
  19. gs *gls.GLS // Reference to OpenGL state
  20. rinfo core.RenderInfo // Preallocated Render info
  21. specs ShaderSpecs // Preallocated Shader specs
  22. sortObjects bool // Flag indicating whether objects should be sorted before rendering
  23. stats Stats // Renderer statistics
  24. // Populated each frame
  25. rgraphics []*graphic.Graphic // Array of rendered graphics
  26. cgraphics []*graphic.Graphic // Array of culled graphics
  27. grmatsOpaque []*graphic.GraphicMaterial // Array of rendered opaque graphic materials for scene
  28. grmatsTransp []*graphic.GraphicMaterial // Array of rendered transparent graphic materials for scene
  29. grmatsGui []*graphic.GraphicMaterial // Array of rendered GUI graphic materials for scene
  30. ipanels []gui.IPanel // Array of all top rendered IPanels (panels which are not children of other IPanels)
  31. ambLights []*light.Ambient // Array of ambient lights for last scene
  32. dirLights []*light.Directional // Array of directional lights for last scene
  33. pointLights []*light.Point // Array of point
  34. spotLights []*light.Spot // Array of spot lights for the scene
  35. others []core.INode // Other nodes (audio, players, etc)
  36. }
  37. // Stats describes how many object types were rendered.
  38. // It is cleared at the start of each render.
  39. type Stats struct {
  40. Graphics int // Number of graphic objects rendered
  41. Lights int // Number of lights rendered
  42. Panels int // Number of Gui panels rendered
  43. Others int // Number of other objects rendered
  44. }
  45. // NewRenderer creates and returns a pointer to a new Renderer.
  46. func NewRenderer(gs *gls.GLS) *Renderer {
  47. r := new(Renderer)
  48. r.gs = gs
  49. r.Shaman.Init(gs)
  50. r.sortObjects = true
  51. r.ambLights = make([]*light.Ambient, 0)
  52. r.dirLights = make([]*light.Directional, 0)
  53. r.pointLights = make([]*light.Point, 0)
  54. r.spotLights = make([]*light.Spot, 0)
  55. r.others = make([]core.INode, 0)
  56. r.rgraphics = make([]*graphic.Graphic, 0)
  57. r.cgraphics = make([]*graphic.Graphic, 0)
  58. r.grmatsOpaque = make([]*graphic.GraphicMaterial, 0)
  59. r.grmatsTransp = make([]*graphic.GraphicMaterial, 0)
  60. r.ipanels = make([]gui.IPanel, 0)
  61. return r
  62. }
  63. // Stats returns a copy of the statistics for the last frame.
  64. // Should be called after the frame was rendered.
  65. func (r *Renderer) Stats() Stats {
  66. return r.stats
  67. }
  68. // SetObjectSorting sets whether objects will be sorted before rendering.
  69. func (r *Renderer) SetObjectSorting(sort bool) {
  70. r.sortObjects = sort
  71. }
  72. // ObjectSorting returns whether objects will be sorted before rendering.
  73. func (r *Renderer) ObjectSorting() bool {
  74. return r.sortObjects
  75. }
  76. // Render renders the specified scene using the specified camera. Returns an an error.
  77. func (r *Renderer) Render(scene core.INode, cam camera.ICamera) error {
  78. // Updates world matrices of all scene nodes
  79. scene.UpdateMatrixWorld()
  80. // Build RenderInfo
  81. cam.ViewMatrix(&r.rinfo.ViewMatrix)
  82. cam.ProjMatrix(&r.rinfo.ProjMatrix)
  83. // Clear stats and scene arrays
  84. r.stats = Stats{}
  85. r.ambLights = r.ambLights[0:0]
  86. r.dirLights = r.dirLights[0:0]
  87. r.pointLights = r.pointLights[0:0]
  88. r.spotLights = r.spotLights[0:0]
  89. r.others = r.others[0:0]
  90. r.rgraphics = r.rgraphics[0:0]
  91. r.cgraphics = r.cgraphics[0:0]
  92. r.grmatsOpaque = r.grmatsOpaque[0:0]
  93. r.grmatsTransp = r.grmatsTransp[0:0]
  94. r.ipanels = r.ipanels[0:0]
  95. // Prepare for frustum culling
  96. var proj math32.Matrix4
  97. proj.MultiplyMatrices(&r.rinfo.ProjMatrix, &r.rinfo.ViewMatrix)
  98. frustum := math32.NewFrustumFromMatrix(&proj)
  99. // Classify scene and all scene nodes, culling renderable IGraphics which are fully outside of the camera frustum
  100. r.classifyAndCull(scene, frustum)
  101. //log.Debug("Rendered/Culled: %v/%v", len(r.grmats), len(r.cgrmats))
  102. // Set light counts in shader specs
  103. r.specs.AmbientLightsMax = len(r.ambLights)
  104. r.specs.DirLightsMax = len(r.dirLights)
  105. r.specs.PointLightsMax = len(r.pointLights)
  106. r.specs.SpotLightsMax = len(r.spotLights)
  107. // Pre-calculate MV and MVP matrices and compile lists of opaque and transparent graphic materials
  108. for _, gr := range r.rgraphics {
  109. // Calculate MV and MVP matrices for all non-GUI graphics to be rendered
  110. gr.CalculateMatrices(r.gs, &r.rinfo)
  111. // Append all graphic materials of this graphic to lists of graphic materials to be rendered
  112. materials := gr.Materials()
  113. for i := range materials {
  114. if materials[i].IMaterial().GetMaterial().Transparent() {
  115. r.grmatsTransp = append(r.grmatsTransp, &materials[i])
  116. } else {
  117. r.grmatsOpaque = append(r.grmatsOpaque, &materials[i])
  118. }
  119. }
  120. }
  121. // TODO: If both GraphicMaterials belong to same Graphic we might want to keep their relative order...
  122. // Z-sort graphic materials (opaque front-to-back and transparent back-to-front)
  123. if r.sortObjects {
  124. zSort(r.grmatsOpaque, false) // Sort opaque graphics front to back
  125. zSort(r.grmatsTransp, true) // Sort transparent graphics back to front
  126. }
  127. // Render GUI elements first
  128. for _, ipan := range r.ipanels {
  129. err := r.renderPanel(ipan)
  130. if err != nil {
  131. return err
  132. }
  133. }
  134. // Render opaque objects (already sorted front to back)
  135. for _, grmat := range r.grmatsOpaque {
  136. err := r.renderGraphicMaterial(grmat)
  137. if err != nil {
  138. return err
  139. }
  140. }
  141. // Render transparent objects (already sorted back to front)
  142. for _, grmat := range r.grmatsTransp {
  143. err := r.renderGraphicMaterial(grmat)
  144. if err != nil {
  145. return err
  146. }
  147. }
  148. // Render other nodes (audio players, etc)
  149. for _, inode := range r.others {
  150. inode.Render(r.gs)
  151. r.stats.Others++
  152. }
  153. // Enable depth mask so that clearing the depth buffer works
  154. r.gs.DepthMask(true)
  155. return nil
  156. }
  157. // classifyAndCull classifies the provided INode and all of its descendents.
  158. // It ignores (culls) renderable IGraphics which are fully outside of the specified frustum.
  159. func (r *Renderer) classifyAndCull(inode core.INode, frustum *math32.Frustum) {
  160. classifyChildren := true
  161. // Ignore invisible nodes and their descendants
  162. if !inode.Visible() {
  163. return
  164. }
  165. // If node is an IPanel append it to appropriate list
  166. if ipan, ok := inode.(gui.IPanel); ok {
  167. r.ipanels = append(r.ipanels, ipan)
  168. classifyChildren = false // Don't classify children since they must be IPanels
  169. // Check if node is an IGraphic
  170. } else if igr, ok := inode.(graphic.IGraphic); ok {
  171. if igr.Renderable() {
  172. gr := igr.GetGraphic()
  173. // Frustum culling
  174. if igr.Cullable() {
  175. mw := gr.MatrixWorld()
  176. geom := igr.GetGeometry()
  177. bb := geom.BoundingBox()
  178. bb.ApplyMatrix4(&mw)
  179. if frustum.IntersectsBox(&bb) {
  180. // Append graphic to list of graphics to be rendered
  181. r.rgraphics = append(r.rgraphics, gr)
  182. } else {
  183. // Append graphic to list of culled graphics
  184. r.cgraphics = append(r.cgraphics, gr)
  185. }
  186. } else {
  187. // Append graphic to list of graphics to be rendered
  188. r.rgraphics = append(r.rgraphics, gr)
  189. }
  190. }
  191. // Node is not a Graphic
  192. } else {
  193. // Check if node is a Light
  194. if il, ok := inode.(light.ILight); ok {
  195. switch l := il.(type) {
  196. case *light.Ambient:
  197. r.ambLights = append(r.ambLights, l)
  198. case *light.Directional:
  199. r.dirLights = append(r.dirLights, l)
  200. case *light.Point:
  201. r.pointLights = append(r.pointLights, l)
  202. case *light.Spot:
  203. r.spotLights = append(r.spotLights, l)
  204. default:
  205. panic("Invalid light type")
  206. }
  207. // Other nodes
  208. } else {
  209. r.others = append(r.others, inode)
  210. }
  211. }
  212. // Classify children (if not an IPanel)
  213. if classifyChildren {
  214. for _, ichild := range inode.Children() {
  215. r.classifyAndCull(ichild, frustum)
  216. }
  217. }
  218. }
  219. // zSort sorts a list of graphic materials based on the
  220. // user-specified order first then based on their Z position relative to the camera
  221. func zSort(grmats []*graphic.GraphicMaterial, backToFront bool) {
  222. sort.Slice(grmats, func(i, j int) bool {
  223. gr1 := grmats[i].IGraphic().GetGraphic()
  224. gr2 := grmats[j].IGraphic().GetGraphic()
  225. // Check for user-supplied render order
  226. rO1 := gr1.RenderOrder()
  227. rO2 := gr2.RenderOrder()
  228. if rO1 != rO2 {
  229. return rO1 < rO2
  230. }
  231. mvm1 := gr1.ModelViewMatrix()
  232. mvm2 := gr2.ModelViewMatrix()
  233. g1pos := gr1.Position()
  234. g2pos := gr2.Position()
  235. g1pos.ApplyMatrix4(mvm1)
  236. g2pos.ApplyMatrix4(mvm2)
  237. if backToFront {
  238. return g1pos.Z < g2pos.Z
  239. }
  240. return g1pos.Z > g2pos.Z
  241. })
  242. }
  243. // renderGraphicMaterial renders the specified graphic material.
  244. func (r *Renderer) renderGraphicMaterial(grmat *graphic.GraphicMaterial) error {
  245. mat := grmat.IMaterial().GetMaterial()
  246. geom := grmat.IGraphic().GetGeometry()
  247. gr := grmat.IGraphic().GetGraphic()
  248. // Add defines from material, geometry and graphic
  249. r.specs.Defines = *gls.NewShaderDefines()
  250. r.specs.Defines.Add(&mat.ShaderDefines)
  251. r.specs.Defines.Add(&geom.ShaderDefines)
  252. r.specs.Defines.Add(&gr.ShaderDefines)
  253. // Set the shader specs for this material and set shader program
  254. r.specs.Name = mat.Shader()
  255. r.specs.ShaderUnique = mat.ShaderUnique()
  256. r.specs.UseLights = mat.UseLights()
  257. r.specs.MatTexturesMax = mat.TextureCount()
  258. // Set active program and apply shader specs
  259. _, err := r.Shaman.SetProgram(&r.specs)
  260. if err != nil {
  261. return err
  262. }
  263. // Set up lights (transfer lights' uniforms)
  264. for idx, l := range r.ambLights {
  265. l.RenderSetup(r.gs, &r.rinfo, idx)
  266. r.stats.Lights++
  267. }
  268. for idx, l := range r.dirLights {
  269. l.RenderSetup(r.gs, &r.rinfo, idx)
  270. r.stats.Lights++
  271. }
  272. for idx, l := range r.pointLights {
  273. l.RenderSetup(r.gs, &r.rinfo, idx)
  274. r.stats.Lights++
  275. }
  276. for idx, l := range r.spotLights {
  277. l.RenderSetup(r.gs, &r.rinfo, idx)
  278. r.stats.Lights++
  279. }
  280. // Render this graphic material
  281. grmat.Render(r.gs, &r.rinfo)
  282. r.stats.Graphics++
  283. return nil
  284. }
  285. // renderPanel renders the specified panel and all its children
  286. // and then sets the panel as not changed.
  287. func (r *Renderer) renderPanel(ipan gui.IPanel) error {
  288. if !ipan.Visible() {
  289. return nil
  290. }
  291. // If panel is renderable, render it
  292. if ipan.Renderable() {
  293. // Set shader specs
  294. grmat := ipan.GetGraphic().Materials()[0]
  295. mat := grmat.IMaterial().GetMaterial()
  296. r.specs.Name = mat.Shader()
  297. r.specs.ShaderUnique = mat.ShaderUnique()
  298. r.specs.UseLights = material.UseLightNone
  299. r.specs.MatTexturesMax = mat.TextureCount()
  300. // Set active program and apply shader specs
  301. _, err := r.Shaman.SetProgram(&r.specs)
  302. if err != nil {
  303. return err
  304. }
  305. // Render this panel's graphic material
  306. grmat.Render(r.gs, &r.rinfo)
  307. r.stats.Panels++
  308. }
  309. // Render children panels
  310. for i := 0; i < len(ipan.Children()); i++ {
  311. err := r.renderPanel(ipan.Children()[i].(gui.IPanel))
  312. if err != nil {
  313. return err
  314. }
  315. }
  316. return nil
  317. }