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/jpass.js | 710 ++++++++++++++++++++++++ 1 file changed, 710 insertions(+) create mode 100644 js/helper-classes/RDGE/src/core/script/jpass.js (limited to 'js/helper-classes/RDGE/src/core/script/jpass.js') diff --git a/js/helper-classes/RDGE/src/core/script/jpass.js b/js/helper-classes/RDGE/src/core/script/jpass.js new file mode 100644 index 00000000..326394dc --- /dev/null +++ b/js/helper-classes/RDGE/src/core/script/jpass.js @@ -0,0 +1,710 @@ +/* +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. +
*/ + +/* + * jpass geometry set - determines the category(s) of geometry that a pass will render + * can be OR'ed together + */ +jpassGeoSet = +{ + 'BACKGROUND' :1, + 'OPAQUE' :2, + 'TRANSPARENT' :4, + 'ADDITIVE' :8, + 'TRANSLUCENT' :16, + 'FOREGROUND' :32, + 'ALL' :127, + 'SCREEN_QUAD' :128, // a screen aligned quad - for rendering a texture to screen + 'SHADOW' :256, // the opaque geometry from shadow light's point of view + 'MAXSETS' :9 +}; + + +/* + * The abstract base class that defines a jpass + * a jpass represents a single render pass of the scene graph + */ +_jpassBaseClass = function() { + this.context = g_Engine.getContext(); + this.renderer = g_Engine.getContext().renderer; + this.sortCats = rdgeConstants.categoryEnumeration; + this.bucketCount = rdgeConstants.categoryEnumeration.MAX_CAT; + + // render order + this.renderOrder = []; + this.renderOrder[this.sortCats.BACKGROUND] = 0; + this.renderOrder[this.sortCats.OPAQUE] = 1; + this.renderOrder[this.sortCats.TRANSPARENT] = 2; + this.renderOrder[this.sortCats.ADDITIVE] = 3 + this.renderOrder[this.sortCats.TRANSLUCENT] = 4; + this.renderOrder[this.sortCats.FOREGROUND] = 5; + + // the name of this pass + this.name = "renderPass_" + nodeIdGen.getId(); + + /* + * if 0 this pass and children are culled from rendering + */ + this.visibility = 1; + + /* + * called when the pass is hidden - override for customication + */ + this.onHide = function() { + + } + + /* + * Called by the system to hide the pass and its children + */ + this.hidePass = function() { + this.onHide(); + + for (var i = 0, len = this.children.length; i < len; ++i) { + this.children[i].hidePass(); + } + } + + /* + * the default output render targets that this pass will create + */ + this.defaultTargetOut = {}; + + /* + * All the outputs required by the pass + */ + this.outputs = + [ + // example + // {'name':"u_mainRT", 'type':"target", 'width':1024, 'height':1024, 'mips':false} + ]; + + /* + * notifies the the renderer that the viewport was modified and needs to be reset + */ + this.dirty = false; + + /* + * Index of the currently selected output target + */ + this.outputIndex = 0; + + /* + * outputs from the previous pass are set as inputs for this pass + */ + this.inputs = + [ + ]; + + /* + * other textures requested for this pass + */ + this.textures = + [ + + ]; + + /* + * the flags that control how the pass is rendered + */ + this.frustum_culling = "enable"; // disable/enable frustum culling during the pass + this.clear = null; // flags to clear the output target with before rendering + this.clearColor = null; + + /* + * Contains a list of geometry to be rendered, during a post process render pass this will usually by a screen quad + */ + this.renderList = + [ + // example + // { 'name':'opaqeobjects', 'geo'{ 'OPAQUE':[ new renderObject(meshNode, transformNode, RenderContext)]} } + ]; + + /* + * The passes that will render after this pass + */ + this.children = []; + + /* + * This shader will override all other shaders + */ + this.shader = null; + + /* + * Technique of from shader to use, if null currently set technique is used + */ + this.technique = null; + + /* + * determines the geometry that will be rendered during the pass + */ + this.geometrySet = "SCREEN_QUAD"; + + + /* + * A camera set here will override any camera active in the scene + */ + this.camera = null; + + /* + * Initialize the pass + */ + this.init = function() { + + } + + /* + * inserts a node into the child map using the pass name as the key + */ + this.insertChildPass = function(jpassObj) { + this.children[jpassObj.name] = jpassObj; + } + + /* + * the scene-graph to process + */ + this.process = function() { + // pre-defined local variables to prevent allocation + var context; + var shaderProg; + var listCount; + var len; + var passes; + var pass; + var node; + var mesh; + var meshCount; + var nodeIdx = 0; + var meshIdx = 0; + var paramIdx = 0; + var passIdx = 0; + + var renderer = g_Engine.getContext().renderer; + + //this.renderer = g_Engine.getContext().renderer; + + // bind output target for rendering + this.bindOutput(); + + // call custom pre-render step + this.preRender(); + + var activeCam = renderer.cameraManager().getActiveCamera(); + + if (this.technique) { + this.shader.setTechnique(this.technique); + } + + renderer.projectionMatrix = activeCam.proj; + + // parameters that can be set once per pass + rdgeGlobalParameters.u_inv_viewport_width.set([1.0 / renderer.vpWidth]); + rdgeGlobalParameters.u_inv_viewport_height.set([1.0 / renderer.vpHeight]); + rdgeGlobalParameters.u_farZ.set([activeCam.zFar()]); + rdgeGlobalParameters.u_projMatrix.set(renderer.projectionMatrix); + + for (var bucketIdx = 0, bckCnt = this.renderList.length; bucketIdx < bckCnt; ++bucketIdx) { + //var curList = this.renderList[bucketIdx]; + listCount = this.renderList[bucketIdx].length; + + for (nodeIdx = 0; nodeIdx < listCount; ++nodeIdx) { + node = this.renderList[bucketIdx][nodeIdx].node; + + if (node.world) { + context = this.renderList[bucketIdx][nodeIdx].context; + shaderProg = this.shader ? this.shader : context.shaderProg; + + renderer.mvMatrix = mat4.mul4x3(node.world, activeCam.view); + renderer.invMvMatrix = mat4.inverse(renderer.mvMatrix); + renderer.normalMatrix = mat4.transpose(renderer.invMvMatrix); + + rdgeGlobalParameters.u_mvMatrix.set(renderer.mvMatrix); + rdgeGlobalParameters.u_normalMatrix.set(renderer.normalMatrix); + rdgeGlobalParameters.u_worldMatrix.set(node.world); + + rdgeGlobalParameters.u_viewMatrix.set(activeCam.view); + rdgeGlobalParameters.u_invViewMatrix.set(mat4.inverse(activeCam.view)); + rdgeGlobalParameters.u_invMvMatrix.set(renderer.invMvMatrix); + + len = context.uniforms.length; + for (paramIdx = 0; paramIdx < len; ++paramIdx) { + rdgeGlobalParameters[context.uniforms[paramIdx].name].set(context.uniforms[paramIdx].value); + } + + // augment the texture list with any post processing textures + this.updateTextureContext(context.textureList); + + shaderProg.setLightContext(context.lights); + shaderProg.setTextureContext(context.textureList); + + passes = shaderProg.begin(); + pass = null; + for (passIdx = 0; passIdx < passes; ++passIdx) { + pass = shaderProg.beginPass(passIdx); + + meshCount = node.meshes.length; + for (meshIdx = 0; meshIdx < meshCount; ++meshIdx) { + mesh = g_meshMan.getModelByName(node.meshes[meshIdx].mesh.name); + + if (mesh) + renderer.drawPrimitive(mesh.primitive, pass.program, pass.attributes); + + } + + shaderProg.endPass(); + + this.onPassEnd(); + } + + shaderProg.end(); + } + } + + } + + // call custom post render step + this.postRender(); + + // remove the bound render target if applicable + this.unbindOutput(); + } + + /* + * handle any setup 'before' processing the geo/scenegraph + * the first step in the pass + */ + this.preRender = function() { + + } + + + /* + * handle any setup 'after' processing the geo/scenegraph + * the last step in the pass + */ + this.postRender = function() { + + } + + + /* + * Custom function to handle any processing in between jshader passes + */ + this.onPassEnd = function() { + if (this.outputIndex + 1 < this.outputs.length) + ++this.outputIndex; + } + + /* + * Set the list of objects to render + */ + this.setRenderList = function(contextList) { + this.renderList = contextList; + } + + /* + * Set the render targets to use as input for the next pass + */ + this.setInputs = function(inputsArr) { + this.inputs = inputsArr.slice(); + } + + /* + * Augment the textureList passed in with the input textures + * this will cause the textures to be bound to the jshader + */ + this.updateTextureContext = function(textureList) { + var inputs = this.inputs.slice(); + for (var i = 0; i < this.inputs.length; ++i) { + textureList.push(inputs[i]); + } + + for (var i = 0; i < this.textures.length; ++i) { + if (this.textures[i].enabled) + textureList.push(this.textures[i]); + } + } + + /* + * If there is an output surface this will bind it + */ + this.bindOutput = function() { + this.outputIndex = 0; + if (this.outputs[this.outputIndex]) { + this.dirty = true; + var oldClear = null; + var fb = this.outputs[this.outputIndex].data[0].frameBuffer; + this.renderer.ctx.bindFramebuffer(this.renderer.ctx.FRAMEBUFFER, fb); + this.renderer.ctx.viewport(0, 0, fb.width, fb.height); + if (this.clearColor) { + oldClear = this.renderer.clearColor; + this.renderer.setClearColor(this.clearColor); + } + + if (this.clear) + this.renderer.clear(this.clear); + else + this.renderer._clear(); + + if (this.clearColor) { + this.renderer.setClearColor(oldClear); + } + } + } + + /* + * If an output surface was bound this will unbind it + */ + this.unbindOutput = function() { + if (this.dirty) { + this.dirty = false; + this.renderer.ctx.bindFramebuffer(this.renderer.ctx.FRAMEBUFFER, null); + this.renderer.setViewPort(0, 0, this.renderer.vpWidth, this.renderer.vpHeight); + } + + } +} + +/* + * The concrete class to be used when creating a scene pass + */ +jpass = function( def ) +{ + // inherit from the base class + this.inheritedFrom = _jpassBaseClass; + this.inheritedFrom(); + + // setup the jpass as defined, recursively instantiated the children as they are encountered + for(var obj in def) + { + this[obj] = def[obj]; + + if(obj == "children") + { + var childCount = this[obj].length; + for(var i = 0; i < childCount; ++i) + { + this[obj][i] = new jpass(this[obj][i]); + } + } + // validate array and create the texture/render-target + else if(obj == "inputs" || obj == "outputs") + { + // this makes sure the data type was passed as an array and converts it to an array if needed + if(!this[obj].slice()) + { + this[obj] = [this[obj]]; + } + + var count = this[obj].length; + for(var i = 0; i < count; ++i) + { + // if the texture/render-target is not created yet do so now + if(!this[obj][i].data) + { + if(this[obj][i].type == "target") + { + // setup the render target + this[obj][i].data = [this.renderer.createRenderTargetTexture(this[obj][i].name, this[obj][i].width, this[obj][i].height, this[obj][i].mips)]; + } + else + { + // make sure data is defined + this[obj][i].data = [null] + } + } + + if(!this[obj][i].handle) + { + this[obj][i].handle = this[obj][i].data[0]; + } + } + } + else if("renderList" == obj) + { + // put the items listed into their buckets + var renderList = new Array(rdgeConstants.categoryEnumeration.MAX_CAT); + renderList[rdgeConstants.categoryEnumeration.BACKGROUND] = []; //BACKGROUND + renderList[rdgeConstants.categoryEnumeration.OPAQUE] = []; //OPAQUE + renderList[rdgeConstants.categoryEnumeration.TRANSPARENT] = []; //TRANSPARENT + renderList[rdgeConstants.categoryEnumeration.ADDITIVE] = []; //ADDITIVE + renderList[rdgeConstants.categoryEnumeration.TRANSLUCENT] = []; //TRANSLUCENT + renderList[rdgeConstants.categoryEnumeration.FOREGROUND] = []; //FOREGROUND + + for(var buckets in this[obj]) + { + // get the enumeration value (ie rdgeConstants.categoryEnumeration.OPAQUE) + var bucket = rdgeConstants.categoryEnumeration[buckets]; + + // create the list for this bucket + + var len = this[obj][buckets].length; + for(var i = 0; i < len; ++i) + { + // add the corresponding objects to the bucket + renderList[bucket].push(this[obj][buckets][i]); + } + + } + + // overwrite with the new list + this[obj] = renderList; + } + else if("shader" == obj) + { + // setup the shader + if(typeof this[obj] == "string") + { + this.shader = new jshader(this[obj]); + } + else + { + var finalShader = new jshader(); + finalShader.def = this[obj]; + finalShader.init(); + this[obj] = finalShader; + } + } + else if("geometrySet" == obj) + { + if( typeof this[obj] == "string") + { + var sets = this[obj].split("|"); + + var setValue = 0; + for(var i = 0; i < sets.length; ++i) + { + setValue |= jpassGeoSet[sets[i]]; + if(sets[i] === "SCREEN_QUAD") + { + this[obj] = jpassGeoSet[sets[i]]; + this.renderList[0] = [new renderObject( createScreenQuadNode() )]; + break; + } + } + + this[obj] = setValue; + } + } + else if("textures" == obj) + { + for(var i = 0, len = this[obj].length; i < len; ++i) + { + if(typeof this[obj][i].data == "string") + { + this[obj][i].name = this[obj][i].data; + this[obj][i].data = [this[obj][i].data]; + this[obj][i].enabled = true; + } + else + { + this[obj][i].name = this.renderer.getTextureByName(this[obj][i].data[0].lookUpName); + } + } + } + + } + + // call initialize code + this.init(); + +} + +/* + * a graph describing the hierarchy of scene pass objects to create the final composition + */ +jpassGraph = function( def ) +{ + // the root pass + this.root = null; + + // going through steps to build the pass graph + if(def && typeof def == "string") + { + // make ajax request + } + else if(def) + { + this.root = new jpass(def); + } + else + { + this.root = new jpass({}); + } + + this.find = null; + this.insert = null; + + // an index to the opaque bucket + this.OPAQUE = rdgeConstants.categoryEnumeration.OPAQUE; + + this.setPassRoot = function(jpassObj) + { + this.root = jpassObj; + } + + /* + * Helper function to generate the references to passes available as passGraph."passName" + */ + this._initHelper = function(node) + { + if(!node) + return; + + this[node.name] = node; + + for(var child in node.children) + { + this._initHelper(node.children[child]); + } + } + this._initHelper(this.root); + + /* + * @param parentName - the name of the parent object to insert under + * @param jpassObj - the jpass object to insert + */ + this.insertAsChild = function(parentName, jpassObj) + { + this.find = parentName; + this.insert = jpassObj; + + // create ref for this object + this[jpassObj.name] = jpassObj; + + this._insertHelper(this.root); + + this.find = null; + this.insert = null; + } + + /* + * Recursive helper function for traversing the graph and insterting the node + */ + this._insertHelper = function(node) + { + if(!node) + return false; + + if(node.name == this.find) + { + node.insertChildPass(this.insert); + return true; + } + + for(var child in node.children) + { + if(this._insertHelper(node.children[child])) + { + break; + } + } + + return true; + } + + // maps jpassGeoSet values to indices + this.geoSetMap = []; + var geoSetIdx = 0; + for(var geoSet in jpassGeoSet) + { + this.geoSetMap[geoSetIdx++] = jpassGeoSet[geoSet]; + } + + /* + * Traverse the render graph, breadth first to produce the final render output + * note: breadth first is used to allow generation of an entire level at once, + * the peers in one level could, in theory, be processed in parallel + */ + this.render = function( sceneGraph ) { + + g_renderStats.numPasses.value=0; + var renderList = sceneGraph.renderList; + var renderer = g_Engine.getContext().renderer; + + // set the first pass with scene geometry + this.root.setRenderList(renderList); + + var curRenderList = null; + + var queue = [ {'parent':this.root } ]; + + var parent = null; + var setCount = this.geoSetMap.length; + var maxCats = rdgeConstants.categoryEnumeration.MAX_CAT; + var idx = 0; + var renderListIdx = 0; + + while(queue.length) + { + var parent = queue.shift().parent; + + // pull out the shadow geometry + if(sceneGraph.shadowsEnabled && parent.geometrySet === jpassGeoSet.SHADOW) + { + parent.setRenderList( [ sceneGraph.shadowRenderList ] ); + } + // screen quad needs special prep since geometry is never re-assigned, lists needs to be cleaned up + else if(parent.geometrySet === jpassGeoSet.SCREEN_QUAD) + { + parent.renderList[0][0].context.textureList = []; + } + // pull out all the sort categories that the pass requested + else + { + curRenderList = []; + renderListIdx = 0; + + // create the pass's render list + for(idx = 0; idx < setCount; ++idx) + { + // assign the render list, checking to make sure the index is less than the total number of material categories + if(parent.geometrySet & this.geoSetMap[idx]) + { + if(renderList[idx]) + { + curRenderList[renderListIdx++] = renderList[idx]; + } + } + } + + parent.setRenderList(curRenderList); + } + + // if this pass has a camera push it on the stack + if(parent.camera) + { + renderer.cameraManager().pushCamera(parent.camera); + } + + // process the geometry - all rendering occurs here + g_renderStats.numPasses.value++; + parent.process(); + + // if this pass had a camera pop it off the stack + if(parent.camera) + { + renderer.cameraManager().popCamera(); + } + + // push on the children + for(idx = parent.children.length - 1; idx >= 0; --idx) + { + if(parent.children[idx].visibility === 1) + { + parent.children[idx].setInputs( parent.outputs ); + queue.unshift({'parent': parent.children[idx] }); + } + else if(parent.children[idx].visibility === 0) + { + parent.children[idx].hidePass(); + parent.children[idx].visibility = 2; + } + } + } + } +} + -- cgit v1.2.3