1 | 'use strict'
|
2 |
|
3 | var coallesceUniforms = require('./reflect')
|
4 |
|
5 | module.exports = createUniformWrapper
|
6 |
|
7 |
|
8 | function identity(x) {
|
9 | var c = new Function('y', 'return function(){return y}')
|
10 | return c(x)
|
11 | }
|
12 |
|
13 | function makeVector(length, fill) {
|
14 | var result = new Array(length)
|
15 | for(var i=0; i<length; ++i) {
|
16 | result[i] = fill
|
17 | }
|
18 | return result
|
19 | }
|
20 |
|
21 |
|
22 | function createUniformWrapper(gl, wrapper, uniforms, locations) {
|
23 |
|
24 | function makeGetter(index) {
|
25 | var proc = new Function(
|
26 | 'gl'
|
27 | , 'wrapper'
|
28 | , 'locations'
|
29 | , 'return function(){return gl.getUniform(wrapper.program,locations[' + index + '])}')
|
30 | return proc(gl, wrapper, locations)
|
31 | }
|
32 |
|
33 | function makePropSetter(path, index, type) {
|
34 | switch(type) {
|
35 | case 'bool':
|
36 | case 'int':
|
37 | case 'sampler2D':
|
38 | case 'samplerCube':
|
39 | return 'gl.uniform1i(locations[' + index + '],obj' + path + ')'
|
40 | case 'float':
|
41 | return 'gl.uniform1f(locations[' + index + '],obj' + path + ')'
|
42 | default:
|
43 | var vidx = type.indexOf('vec')
|
44 | if(0 <= vidx && vidx <= 1 && type.length === 4 + vidx) {
|
45 | var d = type.charCodeAt(type.length-1) - 48
|
46 | if(d < 2 || d > 4) {
|
47 | throw new Error('gl-shader: Invalid data type')
|
48 | }
|
49 | switch(type.charAt(0)) {
|
50 | case 'b':
|
51 | case 'i':
|
52 | return 'gl.uniform' + d + 'iv(locations[' + index + '],obj' + path + ')'
|
53 | case 'v':
|
54 | return 'gl.uniform' + d + 'fv(locations[' + index + '],obj' + path + ')'
|
55 | default:
|
56 | throw new Error('gl-shader: Unrecognized data type for vector ' + name + ': ' + type)
|
57 | }
|
58 | } else if(type.indexOf('mat') === 0 && type.length === 4) {
|
59 | var d = type.charCodeAt(type.length-1) - 48
|
60 | if(d < 2 || d > 4) {
|
61 | throw new Error('gl-shader: Invalid uniform dimension type for matrix ' + name + ': ' + type)
|
62 | }
|
63 | return 'gl.uniformMatrix' + d + 'fv(locations[' + index + '],false,obj' + path + ')'
|
64 | } else {
|
65 | throw new Error('gl-shader: Unknown uniform data type for ' + name + ': ' + type)
|
66 | }
|
67 | break
|
68 | }
|
69 | }
|
70 |
|
71 | function enumerateIndices(prefix, type) {
|
72 | if(typeof type !== 'object') {
|
73 | return [ [prefix, type] ]
|
74 | }
|
75 | var indices = []
|
76 | for(var id in type) {
|
77 | var prop = type[id]
|
78 | var tprefix = prefix
|
79 | if(parseInt(id) + '' === id) {
|
80 | tprefix += '[' + id + ']'
|
81 | } else {
|
82 | tprefix += '.' + id
|
83 | }
|
84 | if(typeof prop === 'object') {
|
85 | indices.push.apply(indices, enumerateIndices(tprefix, prop))
|
86 | } else {
|
87 | indices.push([tprefix, prop])
|
88 | }
|
89 | }
|
90 | return indices
|
91 | }
|
92 |
|
93 | function makeSetter(type) {
|
94 | var code = [ 'return function updateProperty(obj){' ]
|
95 | var indices = enumerateIndices('', type)
|
96 | for(var i=0; i<indices.length; ++i) {
|
97 | var item = indices[i]
|
98 | var path = item[0]
|
99 | var idx = item[1]
|
100 | if(locations[idx]) {
|
101 | code.push(makePropSetter(path, idx, uniforms[idx].type))
|
102 | }
|
103 | }
|
104 | code.push('return obj}')
|
105 | var proc = new Function('gl', 'locations', code.join('\n'))
|
106 | return proc(gl, locations)
|
107 | }
|
108 |
|
109 | function defaultValue(type) {
|
110 | switch(type) {
|
111 | case 'bool':
|
112 | return false
|
113 | case 'int':
|
114 | case 'sampler2D':
|
115 | case 'samplerCube':
|
116 | return 0
|
117 | case 'float':
|
118 | return 0.0
|
119 | default:
|
120 | var vidx = type.indexOf('vec')
|
121 | if(0 <= vidx && vidx <= 1 && type.length === 4 + vidx) {
|
122 | var d = type.charCodeAt(type.length-1) - 48
|
123 | if(d < 2 || d > 4) {
|
124 | throw new Error('gl-shader: Invalid data type')
|
125 | }
|
126 | if(type.charAt(0) === 'b') {
|
127 | return makeVector(d, false)
|
128 | }
|
129 | return makeVector(d, 0)
|
130 | } else if(type.indexOf('mat') === 0 && type.length === 4) {
|
131 | var d = type.charCodeAt(type.length-1) - 48
|
132 | if(d < 2 || d > 4) {
|
133 | throw new Error('gl-shader: Invalid uniform dimension type for matrix ' + name + ': ' + type)
|
134 | }
|
135 | return makeVector(d*d, 0)
|
136 | } else {
|
137 | throw new Error('gl-shader: Unknown uniform data type for ' + name + ': ' + type)
|
138 | }
|
139 | break
|
140 | }
|
141 | }
|
142 |
|
143 | function storeProperty(obj, prop, type) {
|
144 | if(typeof type === 'object') {
|
145 | var child = processObject(type)
|
146 | Object.defineProperty(obj, prop, {
|
147 | get: identity(child),
|
148 | set: makeSetter(type),
|
149 | enumerable: true,
|
150 | configurable: false
|
151 | })
|
152 | } else {
|
153 | if(locations[type]) {
|
154 | Object.defineProperty(obj, prop, {
|
155 | get: makeGetter(type),
|
156 | set: makeSetter(type),
|
157 | enumerable: true,
|
158 | configurable: false
|
159 | })
|
160 | } else {
|
161 | obj[prop] = defaultValue(uniforms[type].type)
|
162 | }
|
163 | }
|
164 | }
|
165 |
|
166 | function processObject(obj) {
|
167 | var result
|
168 | if(Array.isArray(obj)) {
|
169 | result = new Array(obj.length)
|
170 | for(var i=0; i<obj.length; ++i) {
|
171 | storeProperty(result, i, obj[i])
|
172 | }
|
173 | } else {
|
174 | result = {}
|
175 | for(var id in obj) {
|
176 | storeProperty(result, id, obj[id])
|
177 | }
|
178 | }
|
179 | return result
|
180 | }
|
181 |
|
182 |
|
183 | var coallesced = coallesceUniforms(uniforms, true)
|
184 | return {
|
185 | get: identity(processObject(coallesced)),
|
186 | set: makeSetter(coallesced),
|
187 | enumerable: true,
|
188 | configurable: true
|
189 | }
|
190 | }
|