From b89a7ee8b956c96a1dcee995ea840feddc5d4b27 Mon Sep 17 00:00:00 2001 From: Pierre Frisch Date: Thu, 22 Dec 2011 07:25:50 -0800 Subject: First commit of Ninja to ninja-internal Signed-off-by: Valerio Virgillito --- js/helper-classes/RDGE/src/core/script/jshader.js | 750 ++++++++++++++++++++++ 1 file changed, 750 insertions(+) create mode 100644 js/helper-classes/RDGE/src/core/script/jshader.js (limited to 'js/helper-classes/RDGE/src/core/script/jshader.js') diff --git a/js/helper-classes/RDGE/src/core/script/jshader.js b/js/helper-classes/RDGE/src/core/script/jshader.js new file mode 100644 index 00000000..f28219cf --- /dev/null +++ b/js/helper-classes/RDGE/src/core/script/jshader.js @@ -0,0 +1,750 @@ +/* +This file contains proprietary software owned by Motorola Mobility, Inc.
+No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ + +/* +this API should be familiar to anyone who has worked with +HLSL effect files. +*/ + +/* + * A map of types to uniform 'binding' functions + */ +bindMap={}; +bindMap['int'] = function(ctx, a,b) { ctx.uniform1iv(a,b); }; +bindMap['float'] = function(ctx, a,b) { ctx.uniform1fv(a,b); }; +bindMap['vec2'] = function(ctx, a,b) { ctx.uniform2fv(a,b); }; +bindMap['vec3'] = function(ctx, a,b) { ctx.uniform3fv(a,b); }; +bindMap['vec4'] = function(ctx, a,b) { ctx.uniform4fv(a,b); }; +bindMap['mat3'] = function(ctx, a,b) { ctx.uniformMatrix3fv(a,false,b); }; +bindMap['mat4'] = function(ctx, a,b) +{ + ctx.uniformMatrix4fv(a,false,b); + g_Engine.getContext().debug.mat4CallCount++; +}; + +bindMap['tex2d'] = function(ctx, a,b) +{ + ctx.activeTexture(ctx.TEXTURE0+b[0]); + ctx.bindTexture(ctx.TEXTURE_2D, b[1]); + ctx.uniform1iv(a,[b[0]]); +}; + +bindMap['texCube']=function(ctx, a,b) +{ + ctx.activeTexture(ctx.TEXTURE0+b[0]); + ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, b[1]); + ctx.uniform1iv(a,[b[0]]); +}; + +lightDataMap = +[ + function(ctx, loc, lightNode) { ctx.uniform3fv(loc, lightNode.position); }, + function(ctx, loc, lightNode) { ctx.uniform4fv(loc, lightNode.lightDiffuse); }, + function(ctx, loc, lightNode) { ctx.uniform4fv(loc, lightNode.lightAmbient); }, + function(ctx, loc, lightNode) { ctx.uniform4fv(loc, lightNode.lightSpecular); } +]; + +paramTypeNameMapping = null; + +jshader = function(addr) { + this.name = addr; + this.def = null; + this.technique = {}; + this.params = {}; + this.compiledShaders = {}; + this.resetRS = false; + this.currentPass = 0; + this.type_jshader = {}; + this.global = {}; + this.renderer = g_Engine.getContext().renderer; + this.ctx = this.renderer.ctx; + + // load jshader definition at addr (if provided) + if (addr != undefined && addr != null) { + // a synchronous ajax request + request = new XMLHttpRequest(); + request.open("GET", addr, false); + request.send(null); + this.def = JSON.parse(request.responseText); + } + + if (!paramTypeNameMapping) { + var gl = this.ctx; + paramTypeNameMapping = {}; + paramTypeNameMapping[gl.BOOL] = "bool"; + paramTypeNameMapping[gl.INT] = "int"; + paramTypeNameMapping[gl.FLOAT] = "float"; + paramTypeNameMapping[gl.FLOAT_VEC2] = "vec2"; + paramTypeNameMapping[gl.FLOAT_VEC3] = "vec3"; + paramTypeNameMapping[gl.FLOAT_VEC4] = "vec4"; + paramTypeNameMapping[gl.INT_VEC2] = "vec2"; + paramTypeNameMapping[gl.INT_VEC3] = "vec3"; + paramTypeNameMapping[gl.INT_VEC4] = "vec4"; + paramTypeNameMapping[gl.BOOL_VEC2] = "vec2"; + paramTypeNameMapping[gl.BOOL_VEC3] = "vec3"; + paramTypeNameMapping[gl.BOOL_VEC4] = "vec4"; + paramTypeNameMapping[gl.FLOAT_MAT2] = "mat2"; + paramTypeNameMapping[gl.FLOAT_MAT3] = "mat3"; + paramTypeNameMapping[gl.FLOAT_MAT4] = "mat4"; + paramTypeNameMapping[gl.SAMPLER_2D] = "tex2d"; + paramTypeNameMapping[gl.SAMPLER_CUBE] = "texCube"; + } + + /* + * private helper functions + */ + this.bindParameters = function(pass) { + var params = pass.defParamsList; // global parameters to start with + var lightParams = pass.lightParams; + var lightContext = pass.lightContext; + var length = params.length; + var idx = 0; + var texArg = new Array(2) + + // global parameters + var texUnit = 0; + for (idx = 0; idx < length; ++idx) { + if (params[idx].type == 'tex2d' || params[idx].type == 'texCube') { + texArg[0] = texUnit++; + texArg[1] = params[idx].data[0]; + bindMap[params[idx].type](this.ctx, params[idx].loc, texArg); + } + else { + bindMap[params[idx].type](this.ctx, params[idx].loc, rdgeGlobalParameters[params[idx].name].data); + } + } + + // light settings defined by the material + var len = rdgeConstants.MAX_MATERIAL_LIGHTS; + for (var i = 0; i < len; ++i) { + // if there is a context for a light check to see if we have a binding to the light + if (lightContext[i] != null) { + // see if we have parameters to bind to this light + if (lightParams[i]) { + // something is here lets bind it + var numParams = lightParams[i].length; + for (var lp = 0; lp < numParams; ++lp) { + // bind the parameters using the lightDataMap function lookup, dataIndex is the key + lightDataMap[lightParams[i][lp].dataIndex](this.ctx, lightParams[i][lp].loc, lightContext[i]); + } + } + } + } + + // let locally defined uniforms stomp globally defined uniforms + texUnit = this.renderer.usedTextureUnits; // start adding texture after the default textures + params = pass.paramsList; + length = params.length; + for (idx = 0; idx < length; ++idx) { + if (params[idx].type == 'tex2d' || params[idx].type == 'texCube') { + texArg[0] = texUnit++; + texArg[1] = params[idx].data[0]; + bindMap[params[idx].type](this.ctx, params[idx].loc, texArg); + } + else { + bindMap[params[idx].type](this.ctx, params[idx].loc, params[idx].data); + } + } + }; + + /* + * helper function for setting up a texture + */ + createJShaderTexture = function(ctx, param) { + var texHandle = null; + if (typeof param.data == "string") { + texHandle = ctx.canvas.renderer.getTextureByName(param.data, param.wrap, param.repeat, param.mips); + } + else { + texHandle = ctx.canvas.renderer.getTextureByName(param.data.lookUpName, param.wrap, param.repeat, param.mips); + } + + return [texHandle]; + } + + paramType = function(ctx, name, def, program, technique) { + var texUnit = 0; + + // Get the uniform location and store it + this.loc = ctx.getUniformLocation(program, name); + + // if the parameter does not exist in the shader cull it from the pass + if (this.loc == null) { + window.console.log("ctx:" + ctx.canvas.id + ", technique: " + technique + ", uniform: " + name + " was not found, jshader param will have no affect"); + //return; + } + + var param = def[name]; + this.type = param.type; + + // if data was not provided then create default data + if (param.data == undefined) { + switch (param.type) { + case "vec4": this.data = vec4.zero(); break; + case "vec3": this.data = vec3.zero(); break; + case "vec2": this.data = vec2.zero(); break; + case "mat4": this.data = mat4.zero(); break; + case "mat3": this.data = new Array(9); break; + case "mat2": this.data = [0, 0, 0, 0]; break; + case "float": this.data = [0]; break; + case "int": this.data = [0]; break; + case "tex2d": this.data = [ctx.canvas.renderer.getTextureByName("assets/images/white.png")]; break; + case "texCube": this.data = [ctx.canvas.renderer.getTextureByName("assets/images/white.png")]; break; + } + } + else { + if (param.type == 'tex2d' || param.type == 'texCube') { + this.data = createJShaderTexture(ctx, param); + } + else { + this.data = param.data.slice(); + } + } + + this.get = function() { + return this.data.slice(); + } + + this.set = function(v) { + if (this.type == 'tex2d' || this.type == 'texCube') { + if (typeof v == "string") { + v = ctx.canvas.renderer.getTextureByName(v); + } + + this.data[0] = v; + } + else { + var len = this.data.length; + for (var i = 0; i < len; ++i) + this.data[i] = v[i]; + } + } + } + + globalParam = function(ctx, name, param, program) { + this.type = param.type; + + this.data = param.data; + + // Get the uniform location and store it + this.loc = ctx.getUniformLocation(program, name); + + // if data was not provided then create default data + if (!this.data) { + switch (param.type) { + case "vec4": this.data = vec4.zero(); break; + case "vec3": this.data = vec3.zero(); break; + case "vec2": this.data = vec2.zero(); break; + case "mat4": this.data = mat4.zero(); break; + case "mat3": this.data = new Array(9); break; + case "mat2": this.data = [0, 0, 0, 0]; break; + case "float": this.data = [0]; break; + case "int": this.data = [0]; break; + case "tex2d": this.data = [ctx.canvas.renderer.getTextureByName("assets/images/white.png")]; break; + case "texCube": this.data = [ctx.canvas.renderer.getTextureByName("assets/images/white.png")]; break; + } + } + else { + if (param.type == 'tex2d' || param.type == 'texCube') { + this.data = createJShaderTexture(ctx, param); + } + else { + this.data = param.data.slice(); + } + } + + this.get = function() { + return this.data.slice(); + } + this.set = function(v) { + if (this.type == 'tex2d' || this.type == 'texCube') { + if (typeof v == "string") { + v = ctx.canvas.renderer.getTextureByName(v); + } + + this.data[0] = v; + } + else { + var len = this.data.length; + for (var i = 0; i < len; ++i) + this.data[i] = v[i]; + } + + } + } + + + this.init = function() { + var techniques = this.def.techniques; + var defaultTech = null; + for (t in techniques) { + defaultTech = t; + var curTechnique = techniques[t]; + this[t] = + { + 'passes': [] + }; + var numPasses = curTechnique.length; + var i = 0; + while (i < numPasses) { + var program = this.buildProgram(curTechnique[i]); + this.ctx.useProgram(program); + + // automatically create a parameter def for every active attribute in the shader. + var numAttribs = this.ctx.getProgramParameter(program, this.ctx.ACTIVE_ATTRIBUTES); + for (j = 0; j < numAttribs; ++j) { + var attribInfo = this.ctx.getActiveAttrib(program, j); + curTechnique[i].attributes[attribInfo.name] = { 'type': paramTypeNameMapping[attribInfo.type] }; + } + // automatically create a parameter def for every active uniform in the shader. + var numUniforms = this.ctx.getProgramParameter(program, this.ctx.ACTIVE_UNIFORMS); + for (j = 0; j < numUniforms; ++j) { + var uniformInfo = this.ctx.getActiveUniform(program, j); + if (!rdgeGlobalParameters[uniformInfo.name]) { + curTechnique[i].params[uniformInfo.name] = { 'type': paramTypeNameMapping[uniformInfo.type] }; + } + } + + program.ctxId = this.ctx.canvas.id; + if (!program) { + this.renderer.console.log("Build errors found in technique: " + t); + this.def[t] = null; // remove bad technique + break; + } else { + this[t].passes.push({ "program": program, "params": {}, "defParams": {}, "states": curTechnique[i].states, "attributes": curTechnique[i].attribPairs }); + } + + // init default parameters + for (var p in rdgeGlobalParameters) { + var gp = new globalParam(this.ctx, p, rdgeGlobalParameters[p], program); + + if (gp.loc != null) { + gp.loc.ctxID = this.ctx.canvas.id; + this[t].passes[i].defParams[p] = gp; + this.global[p] = gp; + } + } + + // attach light parameters and container to light context + this[t].passes[i].lightParams = [null, null, null, null]; + this[t].passes[i].lightContext = [null, null, null, null]; + + // attach a parameter list that will be used to optimize binding attributes + if (!this[t].passes[i].paramsList) + this[t].passes[i].paramsList = []; + + // locate individual light parameters to bind with local context + var totalLights = rdgeConstants.MAX_MATERIAL_LIGHTS; + for (var lightIdx = 0; lightIdx < totalLights; ++lightIdx) { + + // clear parameter + this[t].passes[i].lightParams[lightIdx] = null; + + // 0 = pos, 1 = diff, 2 = amb, 3 = spec + // this is order assumed for light parameters + + // the parameter index key - lets us know which piece of data we are getting/setting + var lightDataIndex = 0; + + for (var lp in g_Engine.lightManager.lightUniforms[lightIdx]) { + loc = this.ctx.getUniformLocation(program, lp); + + // if item found enable this light param and set parameters to bind and lookup data + if (loc != null) { + if (!this[t].passes[i].lightParams[lightIdx]) + this[t].passes[i].lightParams[lightIdx] = []; + + this[t].passes[i].lightParams[lightIdx].push({ 'loc': loc, 'name': lp, 'dataIndex': lightDataIndex }); + } + + lightDataIndex++; + } + } + + + + // init user defined parameters + for (var p in curTechnique[i].params) { + if (typeof curTechnique[i].params[p] == 'string') { + continue; + } + + var newParam = new paramType(this.ctx, p, curTechnique[i].params, program, t); + + // if(newParam.loc != null) + // { + this[t].passes[i].params[p] = newParam; + this[t][p] = newParam; + // } + } + + // link up aliases + for (var p in curTechnique[i].params) { + if (typeof curTechnique[i].params[p] == 'string') { + // this just redirects to an already existing parameter. + this[t][p] = this[t].passes[i].params[p]; + } + } + + i++; + } + } + + // create linear lists of parameters - optimization + for (t in techniques) { + var numPasses = this[t].passes.length; + + for (var i = 0; i < numPasses; ++i) { + + this[t].passes[i].defParamsList = []; + + for (var p in this[t].passes[i].params) { + var param = this[t].passes[i].params[p]; + param.name = p; + this[t].passes[i].paramsList.push(param); + } + + for (var p in this[t].passes[i].defParams) { + var param = this[t].passes[i].defParams[p]; + param.name = p; + this[t].passes[i].defParamsList.push(param); + } + } + } + + this.setTechnique(defaultTech); + } + + /* + * Init a local parameter at any time during the life of the jshader. + * This will add the parameter to the list of parameters to be bound + * before rendering + */ + this.initLocalParameter = function(name, param) { + var techniques = this.def.techniques; + for (t in techniques) { + var curTechnique = techniques[t]; + var numPasses = curTechnique.length; + var i = 0; + while (i < numPasses) { + var newParam = new paramType(this.ctx, name, param, curTechnique[i].program, t); + if (newParam) { + curTechnique[i][name] = newParam; + + // this params list is created here because a parameter could be added before the jshader is initialized + if (!curTechnique[i].paramsList) + curTechnique[i].paramsList = []; + + curTechnique[i].paramsList.push(newParam); + } + + i++; + } + } + } + + this.buildShader = function(shaderType, shaderStr) { + // pre-pend preprocessor settings + var preProcessor = "#define PC\n" + preProcessor += shaderStr; + shaderStr = preProcessor; + + // Create the shader object + var shader = this.ctx.createShader(shaderType); + if (shader == null) { + this.renderer.console.log("*** Error: unable to create shader '" + shaderType + "'"); + return null; + } + + // Load the shader source + this.ctx.shaderSource(shader, shaderStr); + + // Compile the shader + this.ctx.compileShader(shader); + + // Check the compile status + var compiled = this.ctx.getShaderParameter(shader, this.ctx.COMPILE_STATUS); + if (!compiled) { + // compile failed, report error. + var error = this.ctx.getShaderInfoLog(shader); + window.console.error("*** Error compiling shader '" + shaderType + "':" + error); + this.ctx.deleteShader(shader); + return null; + } + + return shader; + } + + this.buildProgram = function(t) { + window.console.log("building shader pair: <" + t.vshader + ", " + t.fshader + ">"); + var vShaderDef = this.def.shaders[t.vshader]; + var fShaderDef = this.def.shaders[t.fshader]; + + this.ctx.useProgram(null); + + var vertexShader = null; + // if (this.compiledShaders[t.vshader] != undefined) { + // vertexShader = this.compiledShaders[t.vshader]; + // } else + { + var source = null; + + if (vShaderDef.indexOf('{') != -1) { + source = vShaderDef; + } else { + var vshaderRequest = new XMLHttpRequest(); + var urlVertShader = vShaderDef; + vshaderRequest.open("GET", urlVertShader, false); + vshaderRequest.send(null); + source = vshaderRequest.responseText; + } + + vertexShader = this.buildShader(this.ctx.VERTEX_SHADER, source); + + } + + var fragmentShader = null; + // if (this.compiledShaders[t.fshader] != undefined) + // { + // fragmentShader = this.compiledShaders[t.vshader]; + // } else + { + var source = null; + if (vShaderDef.indexOf('{') != -1) { + source = fShaderDef; + } else { + var vshaderRequest = new XMLHttpRequest(); + var urlFragShader = fShaderDef; + vshaderRequest.open("GET", urlFragShader, false); + vshaderRequest.send(null); + source = vshaderRequest.responseText; + } + + fragmentShader = this.buildShader(this.ctx.FRAGMENT_SHADER, source); + } + + if (!vertexShader || !fragmentShader) { + return null; + } + + this.compiledShaders[t.vshader] = vertexShader; + this.compiledShaders[t.fshader] = fragmentShader; + + // Create the program object + var program = this.ctx.createProgram(); + if (!program) { + return null; + } + + // Attach our two shaders to the program + this.ctx.attachShader(program, vertexShader); + this.ctx.attachShader(program, fragmentShader); + + // Bind attributes + var idx = 0; + t.attribPairs = []; + for (var i in t.attributes) { + t.attribPairs.push({ 'loc': idx, 'name': i }); + this.ctx.bindAttribLocation(program, idx++, i); + } + + // Link the program + this.ctx.linkProgram(program); + + // Check the link status + var linked = this.ctx.getProgramParameter(program, this.ctx.LINK_STATUS); + if (!linked) { + // failed to link + var error = this.ctx.getProgramInfoLog(program); + + window.console.log("Error in program linking:" + error); + + this.ctx.deleteProgram(program); + this.ctx.deleteProgram(fragmentShader); + this.ctx.deleteProgram(vertexShader); + + return null; + } + + return program; + } + + /* + * Set the light nodes used by this jshader + * array item 0 corresponds to light 0, item 1 tp light 1 and so on + * place null for lights that are not there + */ + this.setLightContext = function(lightRefArray) { + for (t in this.technique) { + var len = this.technique.passes.length; + for (var i = 0; i < len; ++i) { + this.technique.passes[i].lightContext = lightRefArray.slice(); + } + } + } + + /* + * Called by the system to add material textures settings to the jshader + */ + this.setTextureContext = function(textureList) { + var passCount = this.technique.passes.length; + var param = null; + + for (var t = 0, texCount = textureList.length; t < texCount; ++t) { + for (var i = 0; i < passCount; ++i) { + var param = textureList[t]; + + // set the rdge global parameters if the texture is in the list + if (this.technique.passes[i].defParams[param.name]) + this.technique.passes[i].defParams[param.name].set(param.handle); + + // and set the local parameters if the texture is in the list + if (this.technique.passes[i].params[param.name]) + this.technique.passes[i].params[param.name].set(param.data[0]); + } + } + } + + this.setTechnique = function(name) { + if (this[name] != undefined) { + this.technique = this[name]; + return true; + } + + this.ctx.console.log("Failed to set technique:" + name); + return false; + } + + this.beginRenderState = function(i) { + var states = this.technique.passes[i].states; + if (states == undefined) { + return; + } + + // depth enabled by default. + var depthEnable = states.depthEnable != undefined ? states.depthEnable : true; + if (!depthEnable) { + this.ctx.disable(this.ctx.DEPTH_TEST); + var depthFunc = states.depthFunc != undefined ? states.depthFunc : "LESS"; + this.ctx.depthFunc(this.ctx[states.depthFunc]); + this.ctx.depthMask(true); + } + else { + + if (states.depthFunc) { + this.ctx.depthFunc(this.ctx[states.depthFunc]); + } + + if (states.offset) { + this.ctx.enable(this.ctx.POLYGON_OFFSET_FILL); + this.ctx.polygonOffset(states.offset[0], states.offset[1]); + } + + // depth write + if (states.depthWrite) { + this.ctx.depthMask(states.depthWrite); + } + + if (states.depthRangeMin) { + this.ctx.depthRange(states.depthRangeMin); + } + + if (states.depthRangeMax) { + this.ctx.depthRange(states.depthRangeMax); + } + } + + // blend enabled by default. + var blendEnabled = states.blendEnable != undefined ? states.blendEnable : false; + if (blendEnabled) { + var srcBlend = states.srcBlend != undefined ? states.srcBlend : "ONE"; // default src blend + var dstBlend = states.dstBlend != undefined ? states.dstBlend : "ZERO"; // default dst blend + this.ctx.enable(this.ctx.BLEND); + this.ctx.blendFunc(this.ctx[srcBlend], this.ctx[dstBlend]); + } + + if (states.culling) { + if (states.culling) + this.ctx.enable(this.ctx.CULL_FACE); + else + this.ctx.disable(this.ctx.CULL_FACE); + + } + + if (states.cullFace) { + this.ctx.cullFace(this.ctx[states.cullFace]); + } + + if (states.pointsprite) { + if (states.pointsprite === true) + this.renderer.enablePointSprites(); + else + this.renderer.disablePointSprites(); + } + + this.resetRS = this.technique.passes[i].states.reset == undefined || this.technique.passes[i].states.reset == true; + } + + this.endRenderState = function() { + // restore render states to some default state. + var ctx = this.ctx; + if (this.resetRS) { + ctx.enable(this.ctx.DEPTH_TEST); + ctx.disable(this.ctx.BLEND); + ctx.depthFunc(this.ctx.LESS); + ctx.disable(this.ctx.POLYGON_OFFSET_FILL); + ctx.disable(this.ctx.CULL_FACE); + // this.renderer.disablePointSprites(); + //ctx.enable(ctx.CULL_FACE); + //ctx.cullFace(ctx.BACK); + } + } + + this.begin = function() { + this.currentPass = null; + if (this.def == null || this.technique == null) { + return 0; + } + return this.technique.passes.length; + } + + this.beginPass = function(i) { + this.currentPass = this.technique.passes[i]; + this.ctx.useProgram(this.currentPass.program); + this.bindParameters(this.currentPass); + this.beginRenderState(i); + return this.currentPass; + } + + this.endPass = function() { + this.endRenderState(); + this.ctx.useProgram(null); + } + + this.end = function() { + } + + this.exportShader = function() { + + for (t in this.def.techniques) { + var numPasses = this[t].passes.length; + + for (var i = 0; i < numPasses; ++i) { + this[t].passes[i].paramsList = []; + this[t].passes[i].defParamsList = []; + + for (var p in this[t].passes[i].params) { + var tech = this.def.techniques[t][i]; + if (tech && this[t].passes[i].params[p].type != "tex2d" && this[t].passes[i].params[p] != "texCube") + tech.params[p].data = this[t].passes[i].params[p].data; + } + } + } + + return JSON.stringify(this.def); + + } +} -- cgit v1.2.3