go-opengl-pixel/pixelgl/interface.go

77 lines
2.7 KiB
Go
Raw Normal View History

2016-11-22 16:18:04 -06:00
package pixelgl
// Doer is an interface for manipulating OpenGL state.
2016-11-22 16:18:04 -06:00
//
2016-12-30 10:53:47 -06:00
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For
// example, you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader
// and unuse it, and so on.
2016-11-22 16:18:04 -06:00
//
2016-12-30 10:53:47 -06:00
// This interface provides a clever and flexible way to do it. A typical workflow of an OpenGL
// object is that you enter (load, bind) that object's state, then do something with it, and
// then leave the state. That 'something' in between, let's call it sub (as in subroutine).
2016-11-22 16:18:04 -06:00
//
2016-12-30 10:53:47 -06:00
// The recommended way to implement a Doer is to wrap another Doer (vertex array wraps texture
// and so on), let's call it parent. Then the Do method will look like this:
2016-11-22 16:18:04 -06:00
//
2016-11-28 16:26:56 -06:00
// func (o *MyObject) Do(sub func(Context)) {
// o.parent.Do(func(ctx Context) {
// // enter the object's state
2016-11-28 16:26:56 -06:00
// sub(ctx)
// // leave the object's state
// })
// }
2016-11-22 16:18:04 -06:00
//
2016-12-30 10:53:47 -06:00
// It might seem difficult to grasp this kind of recursion at first, but it's really simple. What
// it's basically saying is: "Hey parent, enter your state, then let me enter mine, then I'll
// do whatever I'm supposed to do in the middle. After that I'll leave my state and please
// leave your state too parent."
2016-11-28 16:26:56 -06:00
//
2016-12-30 10:53:47 -06:00
// Also notice, that the functions are passing a Context around. This context contains the
// most important state variables. Usually, you just pass it as you received it. If you want
// to pass a changed context to your child (e.g. your a shader), use ctx.With* methods.
2016-12-04 13:28:50 -06:00
//
// If possible and makes sense, Do method should be reentrant.
type Doer interface {
2016-11-28 16:26:56 -06:00
Do(sub func(Context))
}
2016-12-30 10:53:47 -06:00
// Context takes state from one object to another. OpenGL is a state machine, so we have
// to approach it like that. However, global variables are evil, so we have Context, that
// transfers important OpenGL state from one object to another.
2016-11-28 16:26:56 -06:00
//
// This type does *not* represent an OpenGL context in the OpenGL terminology.
type Context struct {
2016-12-02 11:15:47 -06:00
shader *Shader
2016-11-28 16:26:56 -06:00
}
// Shader returns the current shader.
func (c Context) Shader() *Shader {
return c.shader
}
// WithShader returns a copy of this context with the specified shader.
func (c Context) WithShader(s *Shader) Context {
return Context{
2016-12-02 11:15:47 -06:00
shader: s,
2016-11-28 16:26:56 -06:00
}
2016-11-22 16:18:04 -06:00
}
2016-12-02 11:15:47 -06:00
// ContextHolder is a root Doer with no parent. It simply forwards a context to a child.
type ContextHolder struct {
Context Context
2016-12-02 11:15:47 -06:00
}
// Do calls sub and passes it the held context.
2016-12-15 17:28:52 -06:00
func (ch ContextHolder) Do(sub func(ctx Context)) {
2016-12-02 11:15:47 -06:00
sub(ch.Context)
}
2016-12-04 16:45:26 -06:00
type noOpDoer struct{}
func (noOpDoer) Do(sub func(ctx Context)) {
sub(Context{})
}
// NoOpDoer is a Doer that just passes an empty context to the caller of Do.
2016-12-04 16:58:19 -06:00
var NoOpDoer Doer = noOpDoer{}