瀏覽代碼

Gui renderer optimizations

leonsal 8 年之前
父節點
當前提交
a94d4b1461
共有 5 個文件被更改,包括 344 次插入10 次删除
  1. 38 0
      core/node.go
  2. 5 0
      gls/gls.go
  3. 1 0
      gui/chart.go
  4. 27 0
      gui/panel.go
  5. 273 10
      renderer/renderer.go

+ 38 - 0
core/node.go

@@ -32,6 +32,7 @@ type Node struct {
 	matrix      math32.Matrix4    // Transform matrix relative to this node parent.
 	matrixWorld math32.Matrix4    // Transform world matrix
 	visible     bool              // Visible flag
+	changed     bool              // Node position/orientation/scale changed
 	parent      INode             // Parent node
 	children    []INode           // Array with node children
 	userData    interface{}       // Generic user data
@@ -59,6 +60,7 @@ func (n *Node) Init() {
 	n.matrixWorld.Identity()
 	n.children = make([]INode, 0)
 	n.visible = true
+	n.changed = true
 }
 
 // GetNode satisfies the INode interface and returns
@@ -154,6 +156,18 @@ func (n *Node) FindLoaderID(id string) INode {
 	return finder(n, id)
 }
 
+// SetChanged sets this node changed flag
+func (n *Node) SetChanged(changed bool) {
+
+	n.changed = changed
+}
+
+// Changed returns this Node changed flag
+func (n *Node) Changed() bool {
+
+	return n.changed
+}
+
 // SetName set an option name for the node.
 // This name can be used for debugging or other purposes.
 func (n *Node) SetName(name string) {
@@ -171,30 +185,35 @@ func (n *Node) Name() string {
 func (n *Node) SetPosition(x, y, z float32) {
 
 	n.position.Set(x, y, z)
+	n.changed = true
 }
 
 // SetPositionVec sets this node position from the specified vector pointer
 func (n *Node) SetPositionVec(vpos *math32.Vector3) {
 
 	n.position = *vpos
+	n.changed = true
 }
 
 // SetPositionX sets the x coordinate of this node position
 func (n *Node) SetPositionX(x float32) {
 
 	n.position.X = x
+	n.changed = true
 }
 
 // SetPositionY sets the y coordinate of this node position
 func (n *Node) SetPositionY(y float32) {
 
 	n.position.Y = y
+	n.changed = true
 }
 
 // SetPositionZ sets the z coordinate of this node position
 func (n *Node) SetPositionZ(z float32) {
 
 	n.position.Z = z
+	n.changed = true
 }
 
 // Position returns the current node position as a vector
@@ -209,6 +228,7 @@ func (n *Node) SetRotation(x, y, z float32) {
 
 	n.rotation.Set(x, y, z)
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // SetRotationX sets the x rotation angle in radians
@@ -217,6 +237,7 @@ func (n *Node) SetRotationX(x float32) {
 
 	n.rotation.X = x
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // SetRotationY sets the y rotation angle in radians
@@ -225,6 +246,7 @@ func (n *Node) SetRotationY(y float32) {
 
 	n.rotation.Y = y
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // SetRotationZ sets the z rotation angle in radians
@@ -233,6 +255,7 @@ func (n *Node) SetRotationZ(z float32) {
 
 	n.rotation.Z = z
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // AddRotationX adds to the current rotation x coordinate in radians
@@ -241,6 +264,7 @@ func (n *Node) AddRotationX(x float32) {
 
 	n.rotation.X += x
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // AddRotationY adds to the current rotation y coordinate in radians
@@ -249,6 +273,7 @@ func (n *Node) AddRotationY(y float32) {
 
 	n.rotation.Y += y
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // AddRotationZ adds to the current rotation z coordinate in radians
@@ -257,6 +282,7 @@ func (n *Node) AddRotationZ(z float32) {
 
 	n.rotation.Z += z
 	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
 }
 
 // Rotation returns the current rotation
@@ -269,18 +295,21 @@ func (n *Node) Rotation() math32.Vector3 {
 func (n *Node) SetQuaternion(x, y, z, w float32) {
 
 	n.quaternion.Set(x, y, z, w)
+	n.changed = true
 }
 
 // SetQuaternionQuat sets this node quaternion from the specified quaternion pointer
 func (n *Node) SetQuaternionQuat(q *math32.Quaternion) {
 
 	n.quaternion = *q
+	n.changed = true
 }
 
 // QuaternionMult multiplies the quaternion by the specified quaternion
 func (n *Node) QuaternionMult(q *math32.Quaternion) {
 
 	n.quaternion.Multiply(q)
+	n.changed = true
 }
 
 // Quaternion returns the current quaternion
@@ -293,30 +322,35 @@ func (n *Node) Quaternion() math32.Quaternion {
 func (n *Node) SetScale(x, y, z float32) {
 
 	n.scale.Set(x, y, z)
+	n.changed = true
 }
 
 // SetScaleVec sets this node scale from a pointer to a Vector3
 func (n *Node) SetScaleVec(scale *math32.Vector3) {
 
 	n.scale = *scale
+	n.changed = true
 }
 
 // SetScaleX sets the X scale of this node
 func (n *Node) SetScaleX(sx float32) {
 
 	n.scale.X = sx
+	n.changed = true
 }
 
 // SetScaleY sets the Y scale of this node
 func (n *Node) SetScaleY(sy float32) {
 
 	n.scale.Y = sy
+	n.changed = true
 }
 
 // SetScaleZ sets the Z scale of this node
 func (n *Node) SetScaleZ(sz float32) {
 
 	n.scale.Z = sz
+	n.changed = true
 }
 
 // Scale returns the current scale
@@ -329,12 +363,14 @@ func (n *Node) Scale() math32.Vector3 {
 func (n *Node) SetDirection(x, y, z float32) {
 
 	n.direction.Set(x, y, z)
+	n.changed = true
 }
 
 // SetDirection sets this node initial direction vector
 func (n *Node) SetDirectionv(vdir *math32.Vector3) {
 
 	n.direction = *vdir
+	n.changed = true
 }
 
 // Direction returns this node initial direction
@@ -347,6 +383,7 @@ func (n *Node) Direction() math32.Vector3 {
 func (n *Node) SetMatrix(m *math32.Matrix4) {
 
 	n.matrix = *m
+	n.changed = true
 }
 
 // Matrix returns a copy of this node local transformation matrix
@@ -359,6 +396,7 @@ func (n *Node) Matrix() math32.Matrix4 {
 func (n *Node) SetVisible(state bool) {
 
 	n.visible = state
+	n.changed = true
 }
 
 // Visible returns the node visibility state

+ 5 - 0
gls/gls.go

@@ -491,6 +491,11 @@ func (gs *GLS) GetShaderiv(shader, pname uint32, params *int32) {
 	C.glGetShaderiv(C.GLuint(shader), C.GLenum(pname), (*C.GLint)(params))
 }
 
+func (gs *GLS) Scissor(x, y int32, width, height uint32) {
+
+	C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
+}
+
 func (gs *GLS) ShaderSource(shader uint32, src string) {
 
 	csource := gs.cbufStr(src)

+ 1 - 0
gui/chart.go

@@ -677,6 +677,7 @@ func (lg *Graph) updateData() {
 		positions.Append(px, py, 0)
 	}
 	lg.vbo.SetBuffer(positions)
+	lg.SetChanged(true)
 }
 
 // recalc recalculates the position and width of the this panel

+ 27 - 0
gui/panel.go

@@ -231,6 +231,7 @@ func (p *Panel) SetTopChild(ipan IPanel) {
 	found := p.Remove(ipan)
 	if found {
 		p.Add(ipan)
+		p.SetChanged(true)
 	}
 }
 
@@ -388,12 +389,14 @@ func (p *Panel) Paddings() BorderSizes {
 func (p *Panel) SetBordersColor(color *math32.Color) {
 
 	p.udata.bordersColor = math32.Color4{color.R, color.G, color.B, 1}
+	p.SetChanged(true)
 }
 
 // SetBordersColor4 sets the color and opacity of this panel borders
 func (p *Panel) SetBordersColor4(color *math32.Color4) {
 
 	p.udata.bordersColor = *color
+	p.SetChanged(true)
 }
 
 // BorderColor4 returns current border color
@@ -406,6 +409,7 @@ func (p *Panel) BordersColor4() math32.Color4 {
 func (p *Panel) SetPaddingsColor(color *math32.Color) {
 
 	p.udata.paddingsColor = math32.Color4{color.R, color.G, color.B, 1}
+	p.SetChanged(true)
 }
 
 // SetColor sets the color of the panel paddings and content area
@@ -413,6 +417,7 @@ func (p *Panel) SetColor(color *math32.Color) *Panel {
 
 	p.udata.paddingsColor = math32.Color4{color.R, color.G, color.B, 1}
 	p.udata.contentColor = p.udata.paddingsColor
+	p.SetChanged(true)
 	return p
 }
 
@@ -421,6 +426,7 @@ func (p *Panel) SetColor4(color *math32.Color4) *Panel {
 
 	p.udata.paddingsColor = *color
 	p.udata.contentColor = *color
+	p.SetChanged(true)
 	return p
 }
 
@@ -514,6 +520,7 @@ func (p *Panel) Bounded() bool {
 func (p *Panel) SetBounded(bounded bool) {
 
 	p.bounded = bounded
+	p.SetChanged(true)
 }
 
 // UpdateMatrixWorld overrides the standard core.Node version which is called by
@@ -562,6 +569,25 @@ func (p *Panel) InsideBorders(x, y float32) bool {
 	return true
 }
 
+// Intersects returns if this panel intersects with the other panel
+func (p *Panel) Intersects(other *Panel) bool {
+
+	pospix := other.Pospix()
+	if p.ContainsPosition(pospix.X, pospix.Y) {
+		return true
+	}
+	if p.ContainsPosition(pospix.X+other.width-1, pospix.Y) {
+		return true
+	}
+	if p.ContainsPosition(pospix.X, pospix.Y+other.height-1) {
+		return true
+	}
+	if p.ContainsPosition(pospix.X+other.width-1, pospix.Y+other.height-1) {
+		return true
+	}
+	return false
+}
+
 // SetEnabled sets the panel enabled state
 // A disabled panel do not process key or mouse events.
 func (p *Panel) SetEnabled(state bool) {
@@ -862,6 +888,7 @@ func (p *Panel) resize(width, height float32, dispatch bool) {
 		float32(p.content.Width) / float32(p.width),
 		float32(p.content.Height) / float32(p.height),
 	}
+	p.SetChanged(true)
 
 	// Update layout and dispatch event
 	if !dispatch {

+ 273 - 10
renderer/renderer.go

@@ -9,12 +9,18 @@ import (
 	"github.com/g3n/engine/core"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/graphic"
+	"github.com/g3n/engine/gui"
 	"github.com/g3n/engine/light"
 )
 
+// Renderer renders a 3D scene and/or a 2D GUI on the current window.
 type Renderer struct {
 	gs          *gls.GLS
 	shaman      Shaman                     // Internal shader manager
+	stats       Stats                      // Renderer statistics
+	scene       core.INode                 // Node containing 3D scene to render
+	panelGui    gui.IPanel                 // Panel containing GUI to render
+	panel3D     gui.IPanel                 // Panel which contains the 3D scene
 	ambLights   []*light.Ambient           // Array of ambient lights for last scene
 	dirLights   []*light.Directional       // Array of directional lights for last scene
 	pointLights []*light.Point             // Array of point
@@ -23,8 +29,19 @@ type Renderer struct {
 	grmats      []*graphic.GraphicMaterial // Array of all graphic materials for scene
 	rinfo       core.RenderInfo            // Preallocated Render info
 	specs       ShaderSpecs                // Preallocated Shader specs
+	redrawGui   bool                       // Flag indicating the gui must be redrawn completely
+	rendered    bool                       // Flag indicating if anything was rendered
+	panList     []gui.IPanel               // list of panels to render
 }
 
+type Stats struct {
+	Graphics int // Number of graphic objects rendered
+	Lights   int // Number of lights rendered
+	Panels   int // Number of Gui panels rendered
+	Others   int // Number of other objects rendered
+}
+
+// NewRenderer creates and returns a pointer to a new Renderer
 func NewRenderer(gs *gls.GLS) *Renderer {
 
 	r := new(Renderer)
@@ -37,39 +54,92 @@ func NewRenderer(gs *gls.GLS) *Renderer {
 	r.spotLights = make([]*light.Spot, 0)
 	r.others = make([]core.INode, 0)
 	r.grmats = make([]*graphic.GraphicMaterial, 0)
-
+	r.panList = make([]gui.IPanel, 0)
 	return r
 }
 
+// AddDefaultShaders adds to this renderer's shader manager all default
+// include chunks, shaders and programs statically registered.
 func (r *Renderer) AddDefaultShaders() error {
 
 	return r.shaman.AddDefaultShaders()
 }
 
+// AddChunk adds a shader chunk with the specified name and source code
 func (r *Renderer) AddChunk(name, source string) {
 
 	r.shaman.AddChunk(name, source)
 }
 
+// AddShader adds a shader program with the specified name and source code
 func (r *Renderer) AddShader(name, source string) {
 
 	r.shaman.AddShader(name, source)
 }
 
+// AddProgram adds a program with the specified name and associated vertex
+// and fragment shaders names (previously registered)
 func (r *Renderer) AddProgram(name, vertex, frag string, others ...string) {
 
 	r.shaman.AddProgram(name, vertex, frag, others...)
 }
 
-//// SetProgramShader sets the shader type and name for a previously specified program name.
-//// Returns error if the specified program or shader name not found or
-//// if an invalid shader type was specified.
-//func (r *Renderer) SetProgramShader(pname string, stype int, sname string) error {
-//
-//	return r.shaman.SetProgramShader(pname, stype, sname)
-//}
+// SetGui sets the gui panel which contains the Gui to render.
+// If set to nil, no Gui will be rendered
+func (r *Renderer) SetGui(gui gui.IPanel) {
+
+	r.panelGui = gui
+}
+
+// SetGuiPanel3D sets the gui panel inside which the 3D scene is shown.
+// This informs the renderer that the Gui elements over this panel
+// must be redrawn even if they didn't change.
+// This panel panel must not be renderable, otherwise it will cover the 3D scene.
+func (r *Renderer) SetGuiPanel3D(panel3D gui.IPanel) {
+
+	r.panel3D = panel3D
+}
+
+// SetScene sets the 3D scene to render
+// If set to nil, no 3D scene will be rendered
+func (r *Renderer) SetScene(scene core.INode) {
+
+	r.scene = scene
+}
+
+// Returns statistics
+func (r *Renderer) Stats() Stats {
+
+	return r.stats
+}
+
+// Render renders the previously set Scene and Gui using the specified camera
+// Returns an indication if anything was rendered and an error
+func (r *Renderer) Render(icam camera.ICamera) (bool, error) {
+
+	r.redrawGui = false
+	r.rendered = false
+	r.stats = Stats{}
+
+	// Renders the 3D scene
+	if r.scene != nil {
+		err := r.renderScene(r.scene, icam)
+		if err != nil {
+			return r.rendered, err
+		}
+	}
+	// Renders the Gui over the 3D scene
+	if r.panelGui != nil {
+		err := r.renderGui()
+		if err != nil {
+			return r.rendered, err
+		}
+	}
+	return r.rendered, nil
+}
 
-func (r *Renderer) Render(iscene core.INode, icam camera.ICamera) error {
+// renderScene renders the 3D scene using the specified camera
+func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 
 	// Updates world matrices of all scene nodes
 	iscene.UpdateMatrixWorld()
@@ -153,11 +223,30 @@ func (r *Renderer) Render(iscene core.INode, icam camera.ICamera) error {
 			continue
 		}
 		r.others[i].Render(r.gs)
+		r.stats.Others++
+	}
+
+	// If there is graphic material to render
+	if len(r.grmats) > 0 {
+		// If the 3D scene to draw is to be confined to user specified panel
+		// sets scissor to avoid erasing gui elements outside of this panel
+		if r.panel3D != nil {
+			pos := r.panel3D.GetPanel().Pospix()
+			width, height := r.panel3D.GetPanel().Size()
+			_, _, _, viewheight := r.gs.GetViewport()
+			r.gs.Enable(gls.SCISSOR_TEST)
+			r.gs.Scissor(int32(pos.X), viewheight-int32(pos.Y)-int32(height), uint32(width), uint32(height))
+		} else {
+			r.gs.Disable(gls.SCISSOR_TEST)
+			r.redrawGui = true
+		}
+		// Clears the area inside the current scissor
+		r.gs.Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT)
+		r.rendered = true
 	}
 
 	// For each *GraphicMaterial
 	for _, grmat := range r.grmats {
-		//log.Debug("grmat:%v", grmat)
 		mat := grmat.GetMaterial().GetMaterial()
 
 		// Sets the shader specs for this material and sets shader program
@@ -173,19 +262,193 @@ func (r *Renderer) Render(iscene core.INode, icam camera.ICamera) error {
 		// Setup lights (transfer lights uniforms)
 		for idx, l := range r.ambLights {
 			l.RenderSetup(r.gs, &r.rinfo, idx)
+			r.stats.Lights++
 		}
 		for idx, l := range r.dirLights {
 			l.RenderSetup(r.gs, &r.rinfo, idx)
+			r.stats.Lights++
 		}
 		for idx, l := range r.pointLights {
 			l.RenderSetup(r.gs, &r.rinfo, idx)
+			r.stats.Lights++
 		}
 		for idx, l := range r.spotLights {
 			l.RenderSetup(r.gs, &r.rinfo, idx)
+			r.stats.Lights++
 		}
 
 		// Render this graphic material
 		grmat.Render(r.gs, &r.rinfo)
+		r.stats.Graphics++
 	}
 	return nil
 }
+
+// renderGui renders the Gui
+func (r *Renderer) renderGui() error {
+
+	// If no 3D scene was rendered sets Gui panels as renderable for background
+	// User must define the colors
+	if len(r.grmats) == 0 {
+		r.panelGui.SetRenderable(true)
+		if r.panel3D != nil {
+			r.panel3D.SetRenderable(true)
+		}
+	} else {
+		r.panelGui.SetRenderable(false)
+		if r.panel3D != nil {
+			r.panel3D.SetRenderable(false)
+		}
+	}
+
+	// Clears list of panels to render
+	r.panList = r.panList[0:0]
+	// Redraw all GUI elements if necessary by appending the GUI panel to the render list
+	if r.redrawGui || r.checkChanged(r.panelGui) {
+		r.appendPanel(r.panelGui)
+	} else {
+		r.buildPanelList()
+	}
+
+	// If there are panels to render
+	if len(r.panList) > 0 {
+		// Updates panels bounds and relative positions
+		r.panelGui.GetPanel().UpdateMatrixWorld()
+		// Disable the scissor test which could have been set by the 3D scene renderer
+		// and then clear the depth buffer, so the panels will be rendered over the 3D scene.
+		r.gs.Disable(gls.SCISSOR_TEST)
+		r.gs.Clear(gls.DEPTH_BUFFER_BIT)
+		r.rendered = true
+	}
+
+	// Render panels
+	for i := 0; i < len(r.panList); i++ {
+		err := r.renderPanel(r.panList[i])
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// buildPanelList builds list of panels over 3D to be rendered
+func (r *Renderer) buildPanelList() {
+
+	// If panel3D not set or renderable, nothing to do
+	if r.panel3D == nil || r.panel3D.Renderable() {
+		return
+	}
+
+	// Internal recursive function to check if any child of the
+	// specified panel is unbounded and over 3D.
+	// If it is, it is inserted in the list of panels to render.
+	var checkUnbounded func(pan *gui.Panel)
+	checkUnbounded = func(pan *gui.Panel) {
+
+		for i := 0; i < len(pan.Children()); i++ {
+			child := pan.Children()[i].(gui.IPanel).GetPanel()
+			if !child.Bounded() && r.checkPanelOver3D(child) {
+				r.appendPanel(child)
+				continue
+			}
+			checkUnbounded(child)
+		}
+	}
+
+	// For all children of the Gui, checks if it is over the 3D panel
+	children := r.panelGui.GetPanel().Children()
+	for i := 0; i < len(children); i++ {
+		pan := children[i].(gui.IPanel).GetPanel()
+		if !pan.Visible() {
+			continue
+		}
+		if r.checkPanelOver3D(pan) {
+			r.appendPanel(pan)
+			continue
+		}
+		// Current child is not over 3D but can have an unbounded child which is
+		checkUnbounded(pan)
+	}
+}
+
+// renderPanel renders the specified panel and all its children
+// and then sets the panel as not changed.
+func (r *Renderer) renderPanel(ipan gui.IPanel) error {
+
+	// If panel not visible, ignore it and all its children
+	pan := ipan.GetPanel()
+	if !pan.Visible() {
+		pan.SetChanged(false)
+		return nil
+	}
+	// If panel is renderable, renders it
+	if pan.Renderable() {
+		// Sets shader program for the panel's material
+		grmat := pan.GetGraphic().Materials()[0]
+		mat := grmat.GetMaterial().GetMaterial()
+		r.specs.Name = mat.Shader()
+		r.specs.ShaderUnique = mat.ShaderUnique()
+		_, err := r.shaman.SetProgram(&r.specs)
+		if err != nil {
+			return err
+		}
+		// Render this panel's graphic material
+		grmat.Render(r.gs, &r.rinfo)
+		r.stats.Panels++
+	}
+	pan.SetChanged(false)
+	// Renders this panel children
+	for i := 0; i < len(pan.Children()); i++ {
+		err := r.renderPanel(pan.Children()[i].(gui.IPanel))
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// appendPanel appends the specified panel to the list of panels to render.
+// Currently there is no need to check for duplicates.
+func (r *Renderer) appendPanel(ipan gui.IPanel) {
+
+	r.panList = append(r.panList, ipan)
+}
+
+// checkChanged checks if the specified panel or any of its children is changed
+func (r *Renderer) checkChanged(ipan gui.IPanel) bool {
+
+	// Unbounded panels are checked even if not visible
+	pan := ipan.GetPanel()
+	if !pan.Bounded() && pan.Changed() {
+		pan.SetChanged(false)
+		return true
+	}
+	// Ignore invisible panel and its children
+	if !pan.Visible() {
+		return false
+	}
+	if pan.Changed() && pan.Renderable() {
+		return true
+	}
+	for i := 0; i < len(pan.Children()); i++ {
+		res := r.checkChanged(pan.Children()[i].(gui.IPanel))
+		if res {
+			return res
+		}
+	}
+	return false
+}
+
+// checkPanelOver3D checks if the specified panel is over
+// the area where the 3D scene will be rendered.
+func (r *Renderer) checkPanelOver3D(ipan gui.IPanel) bool {
+
+	pan := ipan.GetPanel()
+	if !pan.Visible() {
+		return false
+	}
+	if r.panel3D.GetPanel().Intersects(pan) {
+		return true
+	}
+	return false
+}