dreemgl
Version:
DreemGL is an open-source multi-screen prototyping framework for mediated environments, with a visual editor and shader styling for webGL and DALi runtimes written in JavaScript. As a toolkit for gpu-accelerated multiscreen development, DreemGL includes
493 lines (433 loc) • 14.2 kB
JavaScript
/* DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.*/
define.class('$system/base/shader', function(require, exports){
var gltypes = require('$system/base/gltypes')
exports.Texture =
this.Texture = require('./texturewebgl')
this.compileShader = function(gldevice){
var vtx_state = this.vtx_state
var pix_state = this.pix_state
var vtx_code = vtx_state.code
var pix_color = pix_state.code_color
var pix_pick = pix_state.code_pick
var pix_debug = pix_state.code_debug
var gl = gldevice.gl
var cache_id = vtx_code + pix_color + this.has_pick
var shader = gldevice.shadercache[cache_id]
if(shader) return shader
var vtx_shader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vtx_shader, vtx_code)
gl.compileShader(vtx_shader)
if (!gl.getShaderParameter(vtx_shader, gl.COMPILE_STATUS)){
var err = gl.getShaderInfoLog(vtx_shader)
console.error(err.toString(), this.annotateLines(vtx_code))
return
//throw new Error(err)
}
// compile the shader
var pix_color_shader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(pix_color_shader, pix_color)
gl.compileShader(pix_color_shader)
if (!gl.getShaderParameter(pix_color_shader, gl.COMPILE_STATUS)){
var err = gl.getShaderInfoLog(pix_color_shader)
console.error(err.toString(), this.annotateLines(pix_color))
return
//throw new Error(err)
}
shader = gldevice.shadercache[cache_id] = gl.createProgram()
gl.attachShader(shader, vtx_shader)
gl.attachShader(shader, pix_color_shader)
gl.linkProgram(shader)
if (!gl.getProgramParameter(shader, gl.LINK_STATUS)){
var err = gl.getProgramInfoLog(shader)
console.error(err.toString(), this.annotateLines(pix_color))
return
//throw new Error(err)
}
this.getLocations(gl, shader, vtx_state, pix_state)
if(this.compile_use) this.compileUse(shader)
if(pix_debug){
// compile the pick shader
var pix_debug_shader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(pix_debug_shader, pix_debug)
gl.compileShader(pix_debug_shader)
if (!gl.getShaderParameter(pix_debug_shader, gl.COMPILE_STATUS)){
var err = gl.getShaderInfoLog(pix_debug_shader)
console.log(err.toString(), this.annotateLines(pix_debug))
throw new Error(err)
}
shader.debug = gl.createProgram()
gl.attachShader(shader.debug, vtx_shader)
gl.attachShader(shader.debug, pix_debug_shader)
gl.linkProgram(shader.debug)
// add our pick uniform
this.getLocations(gl, shader.debug, vtx_state, pix_state)
if(this.compile_use) this.compileUse(shader.debug)
}
if(this.has_pick){
// compile the pick shader
var pix_pick_shader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(pix_pick_shader, pix_pick)
gl.compileShader(pix_pick_shader)
if (!gl.getShaderParameter(pix_pick_shader, gl.COMPILE_STATUS)){
var err = gl.getShaderInfoLog(pix_pick_shader)
console.log(err.toString(), this.annotateLines(pix_pick))
throw new Error(err)
}
shader.pick = gl.createProgram()
gl.attachShader(shader.pick, vtx_shader)
gl.attachShader(shader.pick, pix_pick_shader)
gl.linkProgram(shader.pick)
// add our pick uniform
pix_state.uniforms['pickguid'] = vec3
pix_state.uniforms['pickalpha'] = float
this.getLocations(gl, shader.pick, vtx_state, pix_state)
if(this.compile_use) this.compileUse(shader.pick)
}
return shader
}
this.useShader = function(gl, shader){
if(!shader) return
if(shader.use) return shader.use(gl, shader, this)
// use the shader
gl.useProgram(shader)
// set uniforms
var uniset = shader.uniset
var unilocs = shader.unilocs
for(var key in uniset){
var loc = unilocs[key]
var split = loc.split
if(split){
for(var i = 0, prop = this; i < split.length; i ++) prop = prop[split[i]]
if(loc.last !== prop){
loc.last = prop
uniset[key](gl, loc.loc, prop)
}
}
else{
var prop = this['_' + key]
//if(this.dbg) console.log(key, prop)
if(loc.last !== prop){
loc.last = prop
uniset[key](gl, loc.loc, prop)
}
}
}
// textures
var texlocs = shader.texlocs
var texid = 0
for(var key in texlocs){
var texinfo = texlocs[key]
var split = texinfo.split
if(split){
for(var texture = this, i = 0; i < split.length; i ++) texture = texture[split[i]]
}
else{
var texture = this['_' + texinfo.name] || this[texinfo.name]
}
// lets fetch the sampler
var gltex = texture[texinfo.samplerid]
// lets do the texture slots correct
if(!gltex){
gltex = texture.createGLTexture(gl, texid, texinfo)
if(!gltex){
gltex = this.default_texture.createGLTexture(gl, texid, texinfo)
}
}
else{
gl.activeTexture(gl.TEXTURE0 + texid)
gl.bindTexture(gl.TEXTURE_2D, gltex)
if(texture.updateid !== gltex.updateid){
texture.updateGLTexture(gl, gltex)
}
}
gl.uniform1i(texinfo.loc, texid)
texid++
}
// set attributes
var attrlocs = shader.attrlocs
var len = 0 // pull the length out of the buffers
var lastbuf
for(var key in attrlocs){
var attrloc = attrlocs[key]
if(attrloc.name){
var buf = this['_' + attrloc.name]
}
else{
var buf = this['_' + key]
}
if(lastbuf !== buf){
lastbuf = buf
if(!buf.glvb) buf.glvb = gl.createBuffer()
if(buf.length > len) len = buf.length
gl.bindBuffer(gl.ARRAY_BUFFER, buf.glvb)
if(!buf.clean){
gl.bufferData(gl.ARRAY_BUFFER, buf.array.buffer, gl.STREAM_DRAW )
buf.clean = true
}
}
var loc = attrloc.loc
gl.enableVertexAttribArray(loc)
if(attrloc.name){ // ok so. lets set the vertexAttribPointer
gl.vertexAttribPointer(loc, attrloc.slots, gl.FLOAT, false, buf.stride, attrloc.offset)
}
else{
gl.vertexAttribPointer(loc, buf.slots, gl.FLOAT, false, buf.stride, 0)
}
}
// set up blend mode
if(this.alpha_blend_eq.op){
var constant = this.constant
if(constant) gl.blendColor(constant[0], constant[1], constant[2], constant.length>3?constant[3]:1)
gl.enable(gl.BLEND)
gl.blendEquationSeparate(this.color_blend_eq.op, this.alpha_blend_eq.op)
gl.blendFuncSeparate(
this.color_blend_eq.src,
this.color_blend_eq.dst,
this.alpha_blend_eq.src,
this.alpha_blend_eq.dst
)
}
else if(this.color_blend_eq.op){
var constant = this.constant
if(constant) gl.blendColor(constant[0], constant[1], constant[2], constant.length>3?constant[3]:1)
gl.enable(gl.BLEND)
gl.blendEquation(this.color_blend_eq.op)
gl.blendFunc(this.color_blend_eq.src, this.color_blend_eq.dst)
}
else{
gl.disable(gl.BLEND)
}
// set up depth test
if(this.depth_test_eq.func > 1){
gl.enable(gl.DEPTH_TEST)
gl.depthFunc(this.depth_test_eq.func)
}
else{
gl.disable(gl.DEPTH_TEST)
}
return len
}
this.compile_use = true
this.useShaderTemplate = function(gl, shader, root){
// use the shader
gl.useProgram(shader)
// set uniforms
SET_UNIFORMS
// textures
TEXTURE_START
var texture = TEXTURE_VALUE
// lets fetch the sampler
var gltex = texture.TEXTURE_SAMPLER
// lets do the texture slots correct
if(!gltex){
if(!texture.createGLTexture) texture = TEXTURE_VALUE = root.Texture.fromStub(texture)
gltex = texture.createGLTexture(gl, TEXTURE_ID, TEXTURE_INFO)
if(!gltex) return 0
gltex.updateid = texture.updateid
}
else{
gl.activeTexture(TEXTUREGL_ID) // gl.TEXTURE0 + TEXTURE_ID
gl.bindTexture(gl.TEXTURE_2D, gltex)
if(texture.updateid !== gltex.updateid){
texture.updateGLTexture(gl, gltex)
}
}
gl.uniform1i(TEXTURE_LOC, TEXTURE_ID)
TEXTURE_END
// attributes
var len = 0 // pull the length out of the buffers
var lastbuf
ATTRLOC_START
var buf = ATTRLOC_BUF
if (!buf) return 0;
if(lastbuf !== buf){
lastbuf = buf
if(buf.length > len) len = buf.length
if(len === 0) return 0
if(!buf.glvb){
buf.glvb = gl.createBuffer()
buf.clean = false
}
gl.bindBuffer(gl.ARRAY_BUFFER, buf.glvb)
if(!buf.clean){
var dt = Date.now()
gl.bufferData(gl.ARRAY_BUFFER, buf.array, gl.STATIC_DRAW)
buf.clean = true
}
}
var loc = ATTRLOC_LOC
gl.enableVertexAttribArray(loc)
ATTRLOC_ATTRIBPTR
ATTRLOC_END
// set up blend mode
if(root.alpha_blend_eq.op){
var constant = root.constant
if(constant) gl.blendColor(constant[0], constant[1], constant[2], constant.length>3? constant[3]: 1)
gl.enable(gl.BLEND)
gl.blendEquationSeparate(root.color_blend_eq.op, root.alpha_blend_eq.op)
gl.blendFuncSeparate(
root.color_blend_eq.src,
root.color_blend_eq.dst,
root.alpha_blend_eq.src,
root.alpha_blend_eq.dst
)
}
else if(root.color_blend_eq.op){
var constant = root.constant
if(constant) gl.blendColor(constant[0], constant[1], constant[2], constant.length>3? constant[3]: 1)
gl.enable(gl.BLEND)
gl.blendEquation(root.color_blend_eq.op)
gl.blendFunc(root.color_blend_eq.src, root.color_blend_eq.dst)
}
else{
gl.disable(gl.BLEND)
}
// set up depth test
if(root.depth_test_eq.func > 1){
//console.log(root.depth_test_eq)
gl.enable(gl.DEPTH_TEST)
gl.depthFunc(root.depth_test_eq.func)
}
else{
gl.disable(gl.DEPTH_TEST)
}
// set up depth mask
if(root.depth_mask === false){
gl.depthMask(false)
}
else{
gl.depthMask(true)
}
return len
}
this.compileUse = function(shader){
// alright lets compile our useShader from
var tpl = this.useShaderTemplate.toString()
tpl = tpl.replace(/^function/, "function useshader_" + (this.view?this.view.constructor.name:'anonymous') + '_shader_' + this.constructor.name)
// ok lets replace shit.
// set uniforms
var out = 'var loc, uni\n'
var uniset = shader.uniset
var unilocs = shader.unilocs
var refattr = shader.refattr
for(var key in uniset){
var loc = unilocs[key]
var split = loc.split
var isattr = key in refattr
if(split){
var name = ''
for(var i = 0; i < split.length; i++){
if(i) name += '.'
var part = split[i]
if(part === 'layout' || isattr && i === split.length - 1) name += '_'
name += part
}
out += '\t\tuni = root.' + name + '\n'
}
else{
out += '\t\tuni = root.'
if(isattr) out += '_'
out += key + '\n'
}
out += '\t\tloc = shader.unilocs.' + key + '\n'
var gen = gltypes.uniform_gen[loc.type]
var call = gen.call
out += 'gl.' + gen.call + '(loc.loc'
if(gen.mat) out += ', false'
if(gen.args == 1) out += ',uni)\n'
if(gen.args == 2) out += ',uni[0], uni[1])\n'
if(gen.args == 3) out += ',uni[0], uni[1], uni[2])\n'
if(gen.args == 4) out += ',uni[0], uni[1], uni[2], uni[3])\n'
if(gen.args === this.loguni) out += 'if(typeof uni === "number")console.log(uni)\n'
}
tpl = tpl.replace(/SET\_UNIFORMS/, out)
tpl = tpl.replace(/TEXTURE\_START([\S\s]*)TEXTURE\_END/, function(m){
var out =''
var body = m.slice(13,-11)
var texlocs = shader.texlocs
var texid = 0
for(var key in texlocs){
var texinfo = texlocs[key]
var split = texinfo.split
var TEXTURE_VALUE =''
if(split){
TEXTURE_VALUE = 'root.' + split.join('.')
}
else{
TEXTURE_VALUE = 'root.' + texinfo.name
}
out += body
.replace(/TEXTURE_VALUE/g, TEXTURE_VALUE)
.replace(/TEXTURE_SAMPLER/, texinfo.samplerid)
.replace(/TEXTURE_ID/g, texid)
.replace(/TEXTURE_LOC/, 'shader.texlocs.' + key+ '.loc')
.replace(/TEXTURE_INFO/, 'shader.texlocs.' + key)
.replace(/TEXTUREGL_ID/g, gltypes.gl.TEXTURE0 + texid)
texid++
}
return out
})
tpl = tpl.replace(/ATTRLOC\_START([\S\s]*)ATTRLOC\_END/, function(m){
var body = m.slice(13,-11)
var out = ''
var attrlocs = shader.attrlocs
var len = 0 // pull the length out of the buffers
var lastbuf
for(var key in attrlocs){
var attrloc = attrlocs[key]
var ATTRLOC_BUF
if(attrloc.name){
ATTRLOC_BUF = 'root.' + attrloc.name
var buf = this[attrloc.name]
}
else{
ATTRLOC_BUF = 'root.' + key
}
var ATTRLOC_LOC = 'shader.attrlocs.' + key +'.loc'
if(attrloc.name){
ATTRLOC_ATTRIBPTR =
'gl.vertexAttribPointer(loc, '+attrloc.slots+', gl.FLOAT, false, buf.stride, '+attrloc.offset+')'
}
else{
ATTRLOC_ATTRIBPTR =
'gl.vertexAttribPointer(loc, buf.slots, gl.FLOAT, false, buf.stride, 0)'
}
out += body
.replace(/ATTRLOC_BUF/, ATTRLOC_BUF)
.replace(/ATTRLOC_LOC/, ATTRLOC_LOC)
.replace(/ATTRLOC_ATTRIBPTR/, ATTRLOC_ATTRIBPTR)
}
return out
})
tpl = tpl.replace(/gl.[A-Z][A-Z0-9_]+/g, function(m){
return gltypes.gl[m.slice(3)]
})
shader.use = new Function('return ' + tpl)()
}
// all draw types
exports.TRIANGLES = this.TRIANGLES = 0x4
exports.LINES = this.LINES = 0x1
exports.LINE_LOOP = this.LINE_LOOP = 0x2
exports.LINE_STRIP = this.LINE_STRIP = 0x3
exports.TRIANGLE_STRIP = this.TRIANGLE_STRIP = 0x5
exports.TRIANGLE_FAN = this.TRIANGLE_FAN = 0x6
this.drawtype_enum = this.TRIANGLES
Object.defineProperty(this, 'drawtype', {set:function(v){
if(typeof v === 'string') this.drawtype_enum = this[v]
else this.drawtype_enum = v
}})
// lets draw ourselves
this.drawArrays = function(devicewebgl, sub, start, end){
//if(this.mydbg) debugger
if(!this.hasOwnProperty('shader') || this.shader === undefined) this.compile(devicewebgl)
if(!this.shader) return
var gl = devicewebgl.gl
var len = this.useShader(gl, sub? this.shader[sub]: this.shader)
if(len) gl.drawArrays(this.drawtype_enum, start || 0, end === undefined?len: end)
return len
}
})