renderer.go 11 KB

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