renderer.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. ambLights []*light.Ambient // Ambient lights in the scene
  26. dirLights []*light.Directional // Directional lights in the scene
  27. pointLights []*light.Point // Point lights in the scene
  28. spotLights []*light.Spot // Spot lights in the scene
  29. others []core.INode // Other nodes (audio, players, etc)
  30. graphics []*graphic.Graphic // Graphics to be rendered
  31. grmatsOpaque []*graphic.GraphicMaterial // Opaque graphic materials to be rendered
  32. grmatsTransp []*graphic.GraphicMaterial // Transparent graphic materials to be rendered
  33. zLayers map[int][]gui.IPanel // All IPanels to be rendered organized by Z-layer
  34. zLayerKeys []int // Z-layers being used (initially in no particular order, sorted later)
  35. }
  36. // Stats describes how many objects of each type are being rendered.
  37. // It is cleared at the start of each render.
  38. type Stats struct {
  39. GraphicMats int // Number of graphic materials 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.graphics = make([]*graphic.Graphic, 0)
  56. r.grmatsOpaque = make([]*graphic.GraphicMaterial, 0)
  57. r.grmatsTransp = make([]*graphic.GraphicMaterial, 0)
  58. r.zLayers = make(map[int][]gui.IPanel)
  59. r.zLayers[0] = make([]gui.IPanel, 0)
  60. r.zLayerKeys = append(r.zLayerKeys, 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.graphics = r.graphics[0:0]
  91. r.grmatsOpaque = r.grmatsOpaque[0:0]
  92. r.grmatsTransp = r.grmatsTransp[0:0]
  93. r.zLayers = make(map[int][]gui.IPanel)
  94. r.zLayers[0] = make([]gui.IPanel, 0)
  95. r.zLayerKeys = r.zLayerKeys[0:1]
  96. r.zLayerKeys[0] = 0
  97. // Prepare for frustum culling
  98. var proj math32.Matrix4
  99. proj.MultiplyMatrices(&r.rinfo.ProjMatrix, &r.rinfo.ViewMatrix)
  100. frustum := math32.NewFrustumFromMatrix(&proj)
  101. // Classify scene and all scene nodes, culling renderable IGraphics which are fully outside of the camera frustum
  102. r.classifyAndCull(scene, frustum, 0)
  103. // Set light counts in shader specs
  104. r.specs.AmbientLightsMax = len(r.ambLights)
  105. r.specs.DirLightsMax = len(r.dirLights)
  106. r.specs.PointLightsMax = len(r.pointLights)
  107. r.specs.SpotLightsMax = len(r.spotLights)
  108. // Pre-calculate MV and MVP matrices and compile initial lists of opaque and transparent graphic materials
  109. for _, gr := range r.graphics {
  110. // Calculate MV and MVP matrices for all non-GUI graphics to be rendered
  111. gr.CalculateMatrices(r.gs, &r.rinfo)
  112. // Append all graphic materials of this graphic to lists of graphic materials to be rendered
  113. materials := gr.Materials()
  114. for i := range materials {
  115. r.stats.GraphicMats++
  116. if materials[i].IMaterial().GetMaterial().Transparent() {
  117. r.grmatsTransp = append(r.grmatsTransp, &materials[i])
  118. } else {
  119. r.grmatsOpaque = append(r.grmatsOpaque, &materials[i])
  120. }
  121. }
  122. }
  123. // TODO: If both GraphicMaterials belong to same Graphic we might want to keep their relative order...
  124. // Z-sort graphic materials back to front
  125. if r.sortObjects {
  126. zSort(r.grmatsOpaque)
  127. zSort(r.grmatsTransp)
  128. }
  129. // Sort zLayers back to front
  130. sort.Ints(r.zLayerKeys)
  131. // Iterate over all panels from back to front, setting Z and adding graphic materials to grmatsTransp/grmatsOpaque
  132. const deltaZ = 0.00001
  133. panZ := float32(-1 + float32(r.stats.Panels)*deltaZ)
  134. for _, k := range r.zLayerKeys {
  135. for _, ipan := range r.zLayers[k] {
  136. // Set panel Z
  137. ipan.SetPositionZ(panZ)
  138. panZ -= deltaZ
  139. // Append the panel's graphic material to lists of graphic materials to be rendered
  140. mat := ipan.GetGraphic().Materials()[0]
  141. if mat.IMaterial().GetMaterial().Transparent() {
  142. r.grmatsTransp = append(r.grmatsTransp, &mat)
  143. } else {
  144. r.grmatsOpaque = append(r.grmatsOpaque, &mat)
  145. }
  146. }
  147. }
  148. // Render opaque objects front to back
  149. for i := len(r.grmatsOpaque) - 1; i >= 0; i-- {
  150. err := r.renderGraphicMaterial(r.grmatsOpaque[i])
  151. if err != nil {
  152. return err
  153. }
  154. }
  155. // Render transparent objects back to front
  156. for _, grmat := range r.grmatsTransp {
  157. err := r.renderGraphicMaterial(grmat)
  158. if err != nil {
  159. return err
  160. }
  161. }
  162. // Render other nodes (audio players, etc)
  163. for _, inode := range r.others {
  164. inode.Render(r.gs)
  165. }
  166. // Enable depth mask so that clearing the depth buffer works
  167. r.gs.DepthMask(true)
  168. // TODO enable color mask, stencil mask?
  169. // TODO clear the buffers for the user, and set the appropriate masks to true before clearing
  170. return nil
  171. }
  172. // classifyAndCull classifies the provided INode and all of its descendents.
  173. // It ignores (culls) renderable IGraphics which are fully outside of the specified frustum.
  174. func (r *Renderer) classifyAndCull(inode core.INode, frustum *math32.Frustum, zLayer int) {
  175. // Ignore invisible nodes and their descendants
  176. if !inode.Visible() {
  177. return
  178. }
  179. // If node is an IPanel append it to appropriate list
  180. if ipan, ok := inode.(gui.IPanel); ok {
  181. zLayer += ipan.ZLayerDelta()
  182. if ipan.Renderable() {
  183. // TODO cull panels
  184. _, ok := r.zLayers[zLayer]
  185. if !ok {
  186. r.zLayerKeys = append(r.zLayerKeys, zLayer)
  187. r.zLayers[zLayer] = make([]gui.IPanel, 0)
  188. }
  189. r.zLayers[zLayer] = append(r.zLayers[zLayer], ipan)
  190. r.stats.Panels++
  191. }
  192. // Check if node is an IGraphic
  193. } else if igr, ok := inode.(graphic.IGraphic); ok {
  194. if igr.Renderable() {
  195. gr := igr.GetGraphic()
  196. // Frustum culling
  197. if igr.Cullable() {
  198. mw := gr.MatrixWorld()
  199. bb := igr.GetGeometry().BoundingBox()
  200. bb.ApplyMatrix4(&mw)
  201. if frustum.IntersectsBox(&bb) {
  202. // Append graphic to list of graphics to be rendered
  203. r.graphics = append(r.graphics, gr)
  204. }
  205. } else {
  206. // Append graphic to list of graphics to be rendered
  207. r.graphics = append(r.graphics, gr)
  208. }
  209. }
  210. // Node is not a Graphic
  211. } else {
  212. // Check if node is a Light
  213. if il, ok := inode.(light.ILight); ok {
  214. switch l := il.(type) {
  215. case *light.Ambient:
  216. r.ambLights = append(r.ambLights, l)
  217. case *light.Directional:
  218. r.dirLights = append(r.dirLights, l)
  219. case *light.Point:
  220. r.pointLights = append(r.pointLights, l)
  221. case *light.Spot:
  222. r.spotLights = append(r.spotLights, l)
  223. default:
  224. panic("Invalid light type")
  225. }
  226. // Other nodes
  227. } else {
  228. r.others = append(r.others, inode)
  229. r.stats.Others++
  230. }
  231. }
  232. // Classify children
  233. for _, ichild := range inode.Children() {
  234. r.classifyAndCull(ichild, frustum, zLayer)
  235. }
  236. }
  237. // zSort sorts a list of graphic materials based on the user-specified render order
  238. // then based on their Z position relative to the camera, back to front.
  239. func zSort(grmats []*graphic.GraphicMaterial) {
  240. sort.Slice(grmats, func(i, j int) bool {
  241. gr1 := grmats[i].IGraphic().GetGraphic()
  242. gr2 := grmats[j].IGraphic().GetGraphic()
  243. // Check for user-supplied render order
  244. rO1 := gr1.RenderOrder()
  245. rO2 := gr2.RenderOrder()
  246. if rO1 != rO2 {
  247. return rO1 < rO2
  248. }
  249. mvm1 := gr1.ModelViewMatrix()
  250. mvm2 := gr2.ModelViewMatrix()
  251. g1pos := gr1.Position()
  252. g2pos := gr2.Position()
  253. g1pos.ApplyMatrix4(mvm1)
  254. g2pos.ApplyMatrix4(mvm2)
  255. return g1pos.Z < g2pos.Z
  256. })
  257. }
  258. // renderGraphicMaterial renders the specified graphic material.
  259. func (r *Renderer) renderGraphicMaterial(grmat *graphic.GraphicMaterial) error {
  260. mat := grmat.IMaterial().GetMaterial()
  261. geom := grmat.IGraphic().GetGeometry()
  262. gr := grmat.IGraphic().GetGraphic()
  263. // Add defines from material, geometry and graphic
  264. r.specs.Defines = *gls.NewShaderDefines()
  265. r.specs.Defines.Add(&mat.ShaderDefines)
  266. r.specs.Defines.Add(&geom.ShaderDefines)
  267. r.specs.Defines.Add(&gr.ShaderDefines)
  268. // Set the shader specs for this material and set shader program
  269. r.specs.Name = mat.Shader()
  270. r.specs.ShaderUnique = mat.ShaderUnique()
  271. r.specs.UseLights = mat.UseLights()
  272. r.specs.MatTexturesMax = mat.TextureCount()
  273. // Set active program and apply shader specs
  274. _, err := r.Shaman.SetProgram(&r.specs)
  275. if err != nil {
  276. return err
  277. }
  278. // Set up lights (transfer lights' uniforms)
  279. if r.specs.UseLights != material.UseLightNone {
  280. if r.specs.UseLights&material.UseLightAmbient != 0 {
  281. for idx, l := range r.ambLights {
  282. l.RenderSetup(r.gs, &r.rinfo, idx)
  283. r.stats.Lights++
  284. }
  285. }
  286. if r.specs.UseLights&material.UseLightDirectional != 0 {
  287. for idx, l := range r.dirLights {
  288. l.RenderSetup(r.gs, &r.rinfo, idx)
  289. r.stats.Lights++
  290. }
  291. }
  292. if r.specs.UseLights&material.UseLightPoint != 0 {
  293. for idx, l := range r.pointLights {
  294. l.RenderSetup(r.gs, &r.rinfo, idx)
  295. r.stats.Lights++
  296. }
  297. }
  298. if r.specs.UseLights&material.UseLightSpot != 0 {
  299. for idx, l := range r.spotLights {
  300. l.RenderSetup(r.gs, &r.rinfo, idx)
  301. r.stats.Lights++
  302. }
  303. }
  304. }
  305. // Render this graphic material
  306. grmat.Render(r.gs, &r.rinfo)
  307. return nil
  308. }