/
render_group.go
165 lines (137 loc) · 4.6 KB
/
render_group.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package graphics
import (
"fmt"
"strings"
gl "github.com/go-gl/gl/v3.3-core/gl"
"github.com/krapulacoders/krapulaengine2/graphics/errors"
)
// RenderGroup implements basic shading handling
type RenderGroup struct {
id string
shaderPgm uint32
vertexShader string
fragmentShader string
shaderPgmNeedsRelink bool
depthTestEnabled bool
depthTestFunc uint32
blendEnabled bool
blendFunc1 uint32
blendFunc2 uint32
impl RenderGroupImplementation
}
// RenderGroupImplementation is an actual implementation of a render group
type RenderGroupImplementation interface {
// Deinit deallocates any resources
Deinit()
InitShader()
Render()
}
// SetShaderFile sets the shader file according to file ending.
// possible endings: .frag or .vert
// The programs are not compiled until the first time they are run.
// The reason it does not compile the programs is to stay away from the graphics thread's business
func (g *RenderGroup) SetShaderFile(shaderFile string) {
if strings.HasSuffix(shaderFile, ".frag") {
g.fragmentShader = shaderFile
} else if strings.HasSuffix(shaderFile, ".vert") {
g.vertexShader = shaderFile
} else {
panic("unknown shader file extension " + shaderFile)
}
g.shaderPgmNeedsRelink = true
}
// GetShaderProgram returns the shader pgm
func (g *RenderGroup) GetShaderProgram() uint32 {
return g.shaderPgm
}
func (g *RenderGroup) activateShaderProgram() {
if g.shaderPgmNeedsRelink {
fmt.Println("initing shader program")
gl.UseProgram(0)
errors.AssertGLError(errors.Critical, "gl.UseProgram")
vertexShaderPgm := getCachedShader(g.vertexShader, gl.VERTEX_SHADER)
fragmentShaderPgm := getCachedShader(g.fragmentShader, gl.FRAGMENT_SHADER)
g.shaderPgm = gl.CreateProgram()
gl.AttachShader(g.shaderPgm, vertexShaderPgm)
errors.AssertGLError(errors.Critical, "gl.AttachShader1")
gl.AttachShader(g.shaderPgm, fragmentShaderPgm)
errors.AssertGLError(errors.Critical, "gl.AttachShader2")
gl.LinkProgram(g.shaderPgm)
errors.AssertGLError(errors.Critical, "gl.LinkProgram")
var status int32
gl.GetProgramiv(g.shaderPgm, gl.LINK_STATUS, &status)
if status == gl.FALSE {
var logLength int32
gl.GetProgramiv(g.shaderPgm, gl.INFO_LOG_LENGTH, &logLength)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetProgramInfoLog(g.shaderPgm, logLength, nil, gl.Str(log))
panic(fmt.Errorf("failed to link program: %v", log))
}
if !gl.IsProgram(g.shaderPgm) {
panic("newly compiled shader pgm is not a shader pgm")
}
// then do first-time binding
g.impl.InitShader()
g.shaderPgmNeedsRelink = false
}
if !gl.IsProgram(g.shaderPgm) {
panic("previously compiled shader pgm is not a shader pgm")
}
errors.AssertGLError(errors.Debug, fmt.Sprintf("before gl.UseProgram(%v)", g.shaderPgm))
gl.UseProgram(g.shaderPgm)
errors.AssertGLError(errors.Critical, fmt.Sprintf("gl.UseProgram(%v)", g.shaderPgm))
}
// Deinit calls the initer to deinitiate
func (g *RenderGroup) Deinit() {
if g.shaderPgm != 0 {
gl.UseProgram(0)
gl.DeleteProgram(g.shaderPgm)
}
g.shaderPgmNeedsRelink = true
g.impl.Deinit()
}
// Render activates the shader program and calls the renderer
func (g *RenderGroup) Render() {
errors.AssertGLError(errors.Debug, "RenderGroup.Render")
g.activateShaderProgram()
if g.depthTestEnabled {
gl.Enable(gl.DEPTH_TEST)
errors.AssertGLError(errors.Debug, "gl enable depth test")
gl.DepthFunc(g.depthTestFunc)
errors.AssertGLError(errors.Debug, "gl set depth func")
gl.DepthMask(true)
} else {
gl.Disable(gl.DEPTH_TEST)
errors.AssertGLError(errors.Debug, "gl disable depth test")
gl.DepthMask(false)
}
if g.blendEnabled {
gl.Enable(gl.BLEND)
errors.AssertGLError(errors.Debug, "gl enable blending")
gl.BlendFunc(g.blendFunc1, g.blendFunc2)
errors.AssertGLError(errors.Debug, "gl set blend func")
} else {
gl.Disable(gl.BLEND)
errors.AssertGLError(errors.Debug, "gl disable blending")
}
g.impl.Render()
}
// SetDepthTestMode sets the depth test details used when rendering this group
func (g *RenderGroup) SetDepthTestMode(enabled bool, depthFunc uint32) {
g.depthTestEnabled = enabled
g.depthTestFunc = depthFunc
}
// SetBlendingMode sets the blending mode used when rendering this group
func (g *RenderGroup) SetBlendingMode(enabled bool, blendFunc1, blendFunc2 uint32) {
g.blendEnabled = enabled
g.blendFunc1 = blendFunc1
g.blendFunc2 = blendFunc2
}
// NewRenderGroup creates a RenderGroup.
func NewRenderGroup(id string, impl RenderGroupImplementation) *RenderGroup {
g := new(RenderGroup)
g.impl = impl
g.shaderPgm = 0
g.shaderPgmNeedsRelink = true
return g
}