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
513 lines (430 loc) • 17.1 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('./textureheadless')
util = require('util');
// HeadlessApi is a static object to access the headless api
HeadlessApi = require('./headless_api')
// HeadlessDreemgl is the interface between dreemgl and headless.
HeadlessShader = require('./headless_shader')
HeadlessActor = require('./headless_actor')
/**
* @method compileShader
* Compiles the shader, or use the cached version.
* Construct a mesh actor if necessary and attach to the view
* The 'this' pointer is an object like border or hardrect
* @param {Object} gldevice Instance of DeviceHeadless
* @return {Object} shader Object containing compiled shader information.
* In webgl this is an object returned by gl.createProgram
* In headless, this is a hash containing a headless_shader object
*/
this.compileShader = function(gldevice){
//console.log('*#*#*#*# compileShader', this.view ? this.view.id : '<SCREEN>');
var vtx_state = this.vtx_state
var pix_state = this.pix_state
var vtx_code = vtx_state.code
//console.log('vtx_code', vtx_code);
//console.log('pix_code', pix_state.code_color);
//console.log('====== vtx_state call', vtx_state.call);
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
// Get the HeadlessShader object from a cache, or build one
shader = gldevice.shadercache[cache_id]
//if(shader) return this.headlessshader
if (!shader) {
// shader is a hash of compiled information and headless objects
shader = {
object_type: '(compiled_shader_data)'
,debug: {}
,pick: {}
,uniset: {}
,unilocks: {}
,refattr: {}
,texlocs: {}
};
// Build a HeadlessShader object and attach to shader
var shadercode = vtx_state.code;
var fragcode = pix_state.code_color;
shader.headlessshader = new HeadlessShader(shadercode, fragcode);
// Build information on uniforms, textures, and attributes
this.getLocations(gl, shader, vtx_state, pix_state)
//console.log('*** COMPILED ***');
//console.log('unilocks', shader.unilocks);
//console.log('uniset', shader.uniset);
//console.log('refattr', shader.refattr);
//console.log('texlocs', shader.texlocs);
//TODO. Remove?
this.compile_use = true
if(this.compile_use) this.compileUse(shader)
gldevice.shadercache[cache_id] = shader
//Not supported by Headless (Yet)
// 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))
// 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
//
// this.getLocations(gl, shader.pick, vtx_state, pix_state)
//
// if(this.compile_use) this.compileUse(shader.pick)
// }
} // if (!this.headlessshader) {
// Build missing headless objects (if they don't exist)
//console.log('shader', shader.object_type, shader);
HeadlessApi.createHeadlessObjects(shader, this); // shader);
return shader
}
this.useShader = function(gl, shader){
if(shader.use) return shader.use(gl, shader, this)
console.log('***************************************************************************************************OLD CODE RUNNING');
}
this.compile_use = true
// Override from shader.js (HEADLESS)
this.mapUniforms = function(gl, shader, uniforms, uniset, unilocs){
// headless uses registerAnimatableProperty to set a uniform
for(var key in uniforms) if(!uniset[key]){
var type = gltypes.getType(uniforms[key])
//uniset[key] = gltypes.uniforms[type]
uniset[key] = gltypes.uniforms[type]
var loc = unilocs[key] = {
type: type,
loc: key // Store the name, not the location
}
if(key.indexOf('_DOT_') !== -1) loc.split = key.split(/_DOT_/)
}
}
this.mapTextures = function(gl, shader, textures, texlocs){
for(var key in textures){
var tex = textures[key]
var loc = texlocs[key] = {
loc: key, // Store the name, not the location
samplerdef: tex.samplerdef,
samplerid: tex.samplerid,
name: tex.name
}
if(tex.name.indexOf('_DOT_') !== -1) loc.split = tex.name.split(/_DOT_/)
}
}
// Template for generated code
// {object} gl stubbed out gl object
// {object} shader compiled shader object, containing headlessshader
// and headlessgeometry. (see compileShader in this file)
// {object} root display object (ex. border, hardrect), containing
// headlessmaterial, headlessrenderer, headlessactor.
this.useShaderTemplate = function(gl, shader, root){
// Create headless objects when first used
if (root && !root.headlessactor) {
HeadlessApi.createHeadlessActor(root, shader);
}
//console.log('useShader', root.view ? root.view.id : 'screen', 'shader', shader.object_type, 'root', root.object_type)
// use the shader
gl.useProgram(shader)
var headless = HeadlessApi.headless
var headlessactor = root ? root.headlessactor : undefined;
var headlessmaterial = root ? root.headlessmaterial : undefined;
var headlessgeometry = root.headlessgeometry;
//console.log('headlessactor', headlessactor.id, 'shader', shader.headlessshader.id, 'geometry', headlessgeometry.id);
// set uniforms
//shader.addUniforms(shader.dreem_shader);
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 (texture.image) {
var ti = TEXTURE_INFO
// Simulate the DALi Sampler
var sampler = {};
//FIX
if (headlessmaterial) {
var index = headlessmaterial.addTexture(texture, ti.loc, sampler);
gltex.texture_index = index;
//console.log('**** **** **** headless.addTexture', ti.loc, texture.image.getWidth(), texture.image.getHeight(), 'return index', index);
}
}
if(!gltex) return 0
}
else{
gl.activeTexture(TEXTUREGL_ID) // gl.TEXTURE0 + TEXTURE_ID
gl.bindTexture(gl.TEXTURE_2D, gltex)
if(texture.updateid !== gltex.updateid){
texture.updateGLTexture(gl, gltex)
if (gltex.texture_index) {
var headlessmaterial = root.headlessmaterial;
headlessmaterial.removeTexture(gltex.texture_index);
gltex.texture_index = undefined;
if (texture.image) {
var ti = TEXTURE_INFO
// Simulate the DALi Sampler
var sampler = {};
var index = headlessmaterial.addTexture(texture.image, ti.loc, sampler);
gltex.texture_index = index;
}
}
}
}
gl.uniform1i(TEXTURE_LOC, TEXTURE_ID)
TEXTURE_END
// attributes
//shader.compileShader(this);
//console.log('---- ---- ---- addAttributeGeometry');
//shader.headlessgeometry.addAttributeGeometry(shader, shader.attrlocs);
var len = 0 // pull the length out of the buffers
var lastbuf
ATTRLOC_START
var buf = ATTRLOC_BUF
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, gl.STATIC_DRAW)
buf.clean = true
}
}
var loc = ATTRLOC_LOC
gl.enableVertexAttribArray(loc)
ATTRLOC_ATTRIBPTR
ATTRLOC_END
var headlessmaterial = root.headlessmaterial;
// set up blend mode
if(root.alpha_blend_eq.op){
//console.log('==== alpha_blend_eq.op');
var constant = root.constant
if(constant) {
//console.log('CONSTANT', constant[0], constant[1], constant[2], constant[3], 'Not implemented');
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
)
// HEADLESS
//console.log('*** full blend');
headlessmaterial.setBlendMode('BLENDING_ON');
headlessmaterial.setBlendEquation (root.color_blend_eq.op, root.color_blend_eq.op);
headlessmaterial.setBlendFunc(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){
//console.log('BLEND', root.color_blend_eq);
var constant = root.constant
if (constant) {
//console.log('CONSTANT 2', constant[0], constant[1], constant[2], constant[3], 'Not implemented');
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)
headlessmaterial.setBlendMode('BLENDING_ON');
headlessmaterial.setBlendEquation (root.color_blend_eq.op, root.color_blend_eq.op);
//TODO Check this. What are the last two args?
headlessmaterial.setBlendFunc(root.color_blend_eq.src, root.color_blend_eq.dst, root.color_blend_eq.src, root.color_blend_eq.dst);
//headlessmaterial.setBlendFunc(headless.BLEND_FACTOR_SRC_COLOR, headless.BLEND_FACTOR_DST_COLOR, root.color_blend_eq.src, root.color_blend_eq.dst);
//headlessmaterial.setBlendFunc(root.color_blend_eq.src, root.color_blend_eq.dst, headless.BLEND_FACTOR_ONE, headless.BLEND_FACTOR_ZERO);
//headlessmaterial.setBlendFunc(root.color_blend_eq.src, root.color_blend_eq.dst, headless.BLEND_FACTOR_SRC_ALPHA, headless.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
//headlessmaterial.setBlendFunc(root.color_blend_eq.src, root.color_blend_eq.dst, headless.BLEND_FACTOR_CONSTANT_ALPHA, headless.BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA);
}
else{
//console.log('==== blend disabled');
gl.disable(gl.BLEND)
//HEADLESS
headlessmaterial.setBlendMode('BLENDING_OFF');
}
// set up depth test
if(root.depth_test_eq.func){
console.log('***Depth test enabled. NOT IMPLEMENTED');
gl.enable(gl.DEPTH_TEST)
gl.depthFunc(root.depth_test_eq.func)
}
else{
gl.disable(gl.DEPTH_TEST)
}
return len
}
// {object} shader HeadlessShader object, amended with location information
// (see getLocations call above)
// The 'this' pointer is a view
this.compileUse = function(shader){
// Make sure the object has headless
//HeadlessApi.createHeadlessObjects(this.shader);
//console.log('*****compileUse', shader.object_type, this.object_type);
// 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 uniset = shader.uniset
var unilocs = shader.unilocs
var refattr = shader.refattr
var out = 'var loc, uni\n'
out += 'var actor = root.headlessactor\n'
out += 'if (shader && actor) {\n'
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]
//if(gen.args == 1){
var call = gen.call
out += '\t\tvar val = actor.setUniformValue(\'' + key + '\',uni)\n'
}
out += '}\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/g, texinfo.samplerid)
.replace(/TEXTURE_ID/g, texid)
.replace(/TEXTURE_LOC/g, 'shader.texlocs.' + key+ '.loc')
.replace(/TEXTURE_INFO/g, 'shader.texlocs.' + key)
.replace(/TEXTUREGL_ID/g, gltypes.gl.TEXTURE0 + texid)
}
//console.log('=-=-=-=-=- texlocs', out);
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
if (Object.keys(attrlocs).length > 0) {
//console.log('*************************** attrlocs ***********************');
//console.log(attrlocs);
//console.log('************************************************************');
out += 'root.headlessgeometry.addAttributeGeometry(root, shader.attrlocs)\n'
}
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 =
'if(buf.slots>4)debugger;gl.vertexAttribPointer(loc, buf.slots, gl.FLOAT, false, buf.stride, 0)'
}
//HACK. Setting this for text causes problems with image display
ATTRLOC_ATTRIBPTR = ''
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
this.drawtype = HeadlessApi.Geometry.TRIANGLES
// lets draw ourselves.
// A view (the this pointer) makes one call to drawArrays for each shader.
// A typical number is two (one for border and one for hardimage
this.drawArrays = function(devicegl, sub, start, end){
//if (this.texture) {
// console.trace('drawArrays');
//}
//console.trace('*****drawArrays', (this.view? this.view.id : '<screen>'));
// console.log('PROG', this.vtx_state.code);
//if(this.mydbg) debugger
if(!this.hasOwnProperty('shader') || this.shader === undefined) this.compile(devicegl)
var gl = devicegl.gl
var shader = sub? this.shader[sub]: this.shader;
// Attach the headless_obj to the shader.shader object (HEADLESS)
var len = this.useShader(gl, shader);
if(len) gl.drawArrays(this.drawtype, start || 0, end === undefined?len: end)
}
})