1 | 'use strict'
|
2 |
|
3 | var createUniformWrapper = require('./lib/create-uniforms')
|
4 | var createAttributeWrapper = require('./lib/create-attributes')
|
5 | var makeReflect = require('./lib/reflect')
|
6 | var shaderCache = require('./lib/shader-cache')
|
7 | var runtime = require('./lib/runtime-reflect')
|
8 |
|
9 |
|
10 | function Shader(gl) {
|
11 | this.gl = gl
|
12 |
|
13 |
|
14 | this._vref =
|
15 | this._fref =
|
16 | this._relink =
|
17 | this.vertShader =
|
18 | this.fragShader =
|
19 | this.program =
|
20 | this.attributes =
|
21 | this.uniforms =
|
22 | this.types = null
|
23 | }
|
24 |
|
25 | var proto = Shader.prototype
|
26 |
|
27 | proto.bind = function() {
|
28 | if(!this.program) {
|
29 | this._relink()
|
30 | }
|
31 | this.gl.useProgram(this.program)
|
32 | }
|
33 |
|
34 | proto.dispose = function() {
|
35 | if(this._fref) {
|
36 | this._fref.dispose()
|
37 | }
|
38 | if(this._vref) {
|
39 | this._vref.dispose()
|
40 | }
|
41 | this.attributes =
|
42 | this.types =
|
43 | this.vertShader =
|
44 | this.fragShader =
|
45 | this.program =
|
46 | this._relink =
|
47 | this._fref =
|
48 | this._vref = null
|
49 | }
|
50 |
|
51 | function compareAttributes(a, b) {
|
52 | if(a.name < b.name) {
|
53 | return -1
|
54 | }
|
55 | return 1
|
56 | }
|
57 |
|
58 |
|
59 | proto.update = function(
|
60 | vertSource
|
61 | , fragSource
|
62 | , uniforms
|
63 | , attributes) {
|
64 |
|
65 |
|
66 | if(!fragSource || arguments.length === 1) {
|
67 | var obj = vertSource
|
68 | vertSource = obj.vertex
|
69 | fragSource = obj.fragment
|
70 | uniforms = obj.uniforms
|
71 | attributes = obj.attributes
|
72 | }
|
73 |
|
74 | var wrapper = this
|
75 | var gl = wrapper.gl
|
76 |
|
77 |
|
78 | var pvref = wrapper._vref
|
79 | wrapper._vref = shaderCache.shader(gl, gl.VERTEX_SHADER, vertSource)
|
80 | if(pvref) {
|
81 | pvref.dispose()
|
82 | }
|
83 | wrapper.vertShader = wrapper._vref.shader
|
84 | var pfref = this._fref
|
85 | wrapper._fref = shaderCache.shader(gl, gl.FRAGMENT_SHADER, fragSource)
|
86 | if(pfref) {
|
87 | pfref.dispose()
|
88 | }
|
89 | wrapper.fragShader = wrapper._fref.shader
|
90 |
|
91 |
|
92 | if(!uniforms || !attributes) {
|
93 |
|
94 |
|
95 | var testProgram = gl.createProgram()
|
96 | gl.attachShader(testProgram, wrapper.fragShader)
|
97 | gl.attachShader(testProgram, wrapper.vertShader)
|
98 | gl.linkProgram(testProgram)
|
99 | if(!gl.getProgramParameter(testProgram, gl.LINK_STATUS)) {
|
100 | var errLog = gl.getProgramInfoLog(testProgram)
|
101 | console.error('gl-shader: Error linking program:', errLog)
|
102 | throw new Error('gl-shader: Error linking program:' + errLog)
|
103 | }
|
104 |
|
105 |
|
106 | uniforms = uniforms || runtime.uniforms(gl, testProgram)
|
107 | attributes = attributes || runtime.attributes(gl, testProgram)
|
108 |
|
109 |
|
110 | gl.deleteProgram(testProgram)
|
111 | }
|
112 |
|
113 |
|
114 |
|
115 | attributes = attributes.slice()
|
116 | attributes.sort(compareAttributes)
|
117 |
|
118 |
|
119 | var attributeUnpacked = []
|
120 | var attributeNames = []
|
121 | var attributeLocations = []
|
122 | for(var i=0; i<attributes.length; ++i) {
|
123 | var attr = attributes[i]
|
124 | if(attr.type.indexOf('mat') >= 0) {
|
125 | var size = attr.type.charAt(attr.type.length-1)|0
|
126 | var locVector = new Array(size)
|
127 | for(var j=0; j<size; ++j) {
|
128 | locVector[j] = attributeLocations.length
|
129 | attributeNames.push(attr.name + '[' + j + ']')
|
130 | if(typeof attr.location === 'number') {
|
131 | attributeLocations.push(attr.location + j)
|
132 | } else if(Array.isArray(attr.location) &&
|
133 | attr.location.length === size &&
|
134 | typeof attr.location[j] === 'number') {
|
135 | attributeLocations.push(attr.location[j]|0)
|
136 | } else {
|
137 | attributeLocations.push(-1)
|
138 | }
|
139 | }
|
140 | attributeUnpacked.push({
|
141 | name: attr.name,
|
142 | type: attr.type,
|
143 | locations: locVector
|
144 | })
|
145 | } else {
|
146 | attributeUnpacked.push({
|
147 | name: attr.name,
|
148 | type: attr.type,
|
149 | locations: [ attributeLocations.length ]
|
150 | })
|
151 | attributeNames.push(attr.name)
|
152 | if(typeof attr.location === 'number') {
|
153 | attributeLocations.push(attr.location|0)
|
154 | } else {
|
155 | attributeLocations.push(-1)
|
156 | }
|
157 | }
|
158 | }
|
159 |
|
160 |
|
161 | var curLocation = 0
|
162 | for(var i=0; i<attributeLocations.length; ++i) {
|
163 | if(attributeLocations[i] < 0) {
|
164 | while(attributeLocations.indexOf(curLocation) >= 0) {
|
165 | curLocation += 1
|
166 | }
|
167 | attributeLocations[i] = curLocation
|
168 | }
|
169 | }
|
170 |
|
171 |
|
172 | var uniformLocations = new Array(uniforms.length)
|
173 | function relink() {
|
174 | wrapper.program = shaderCache.program(
|
175 | gl
|
176 | , wrapper._vref
|
177 | , wrapper._fref
|
178 | , attributeNames
|
179 | , attributeLocations)
|
180 |
|
181 | for(var i=0; i<uniforms.length; ++i) {
|
182 | uniformLocations[i] = gl.getUniformLocation(
|
183 | wrapper.program
|
184 | , uniforms[i].name)
|
185 | }
|
186 | }
|
187 |
|
188 |
|
189 | relink()
|
190 |
|
191 |
|
192 | wrapper._relink = relink
|
193 |
|
194 |
|
195 | wrapper.types = {
|
196 | uniforms: makeReflect(uniforms),
|
197 | attributes: makeReflect(attributes)
|
198 | }
|
199 |
|
200 |
|
201 | wrapper.attributes = createAttributeWrapper(
|
202 | gl
|
203 | , wrapper
|
204 | , attributeUnpacked
|
205 | , attributeLocations)
|
206 |
|
207 |
|
208 | Object.defineProperty(wrapper, 'uniforms', createUniformWrapper(
|
209 | gl
|
210 | , wrapper
|
211 | , uniforms
|
212 | , uniformLocations))
|
213 | }
|
214 |
|
215 |
|
216 | function createShader(
|
217 | gl
|
218 | , vertSource
|
219 | , fragSource
|
220 | , uniforms
|
221 | , attributes) {
|
222 |
|
223 | var shader = new Shader(gl)
|
224 |
|
225 | shader.update(
|
226 | vertSource
|
227 | , fragSource
|
228 | , uniforms
|
229 | , attributes)
|
230 |
|
231 | return shader
|
232 | }
|
233 |
|
234 | module.exports = createShader |
\ | No newline at end of file |