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 --- .../RDGE/src/core/script/scenegraph.js | 1123 ++++++++++++++++++++ 1 file changed, 1123 insertions(+) create mode 100644 js/helper-classes/RDGE/src/core/script/scenegraph.js (limited to 'js/helper-classes/RDGE/src/core/script/scenegraph.js') diff --git a/js/helper-classes/RDGE/src/core/script/scenegraph.js b/js/helper-classes/RDGE/src/core/script/scenegraph.js new file mode 100644 index 00000000..6814dcbf --- /dev/null +++ b/js/helper-classes/RDGE/src/core/script/scenegraph.js @@ -0,0 +1,1123 @@ +/* +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. +
*/ + +var g_drawCount = 0; +g_enableFlyCam = false; + +g_sceneStats = {}; +g_sceneStats.nodesVisible = new stat("scenegraph", "nodesVisible", 0, null, false); +g_sceneStats.nodesCulled = new stat("scenegraph", "nodesCulled", 0, null, false); +g_sceneStats.nodesVisited = new stat("scenegraph", "nodesVisited", 0, null, false); +g_sceneStats.reset = function() { + g_sceneStats.nodesVisible.value = 0; + g_sceneStats.nodesVisited.value = 0; + g_sceneStats.nodesCulled.value = 0; +}; + +// default render proc +// render scene nodes +function DefaultRender() +{ + // setup the default shader + this.renderer = g_Engine.getContext().renderer; + //this.shaderProgram = this.renderer.defaultShader; + this.jshaderProgram = new jshader(); + this.jshaderProgram.def = this.renderer.defaultShaderDefintion; + this.jshaderProgram.init(); +} + +DefaultRender.prototype.process=function(context,trNode,parent) { + +} + +function renderObject( trNode, renderCtx, parent) +{ + this.node = trNode ? trNode : createTransformNode(); + this.context = renderCtx ? renderCtx : new RenderContext(); + this.parent = parent ? parent : null; +} + + +// Scene graph contains set of tools for manipulating the graph +function SceneGraph(scene) +{ + if(scene == undefined || scene == null) + scene = {}; + + this.scene = scene; + + if(this.scene.root != undefined) + this.scene = this.scene.root; + else + this.scene = { 'children':[] }; + + this.tick = 0; + this.lastTick = -1; + this.frustumCulling = true; + + // the main light for the scene - also casts shadows + this.mainLight = new shadowLight(); + + this.bckTypes = rdgeConstants.categoryEnumeration; + + this.renderList = new Array(this.bckTypes.MAX_CAT); + this.renderList[this.bckTypes.BACKGROUND] = []; //BACKGROUND + this.renderList[this.bckTypes.OPAQUE] = []; //OPAQUE + this.renderList[this.bckTypes.TRANSPARENT] = []; //TRANSPARENT + this.renderList[this.bckTypes.ADDITIVE] = []; //ADDITIVE + this.renderList[this.bckTypes.TRANSLUCENT] = []; //TRANSLUCENT + this.renderList[this.bckTypes.FOREGROUND] = []; //FOREGROUND + + // a list of shadow geometry + this.shadowRenderList = []; + + // scene traversal functor for creating a culled list of shadow geometry + this.shadowCuller = null; + + // define passes to render geometry and handle post-processing + this.defaultPassDef = + { + // a pass can have children that will receive their parents output as input + + // this pass renders the depth map to an off-screen target - from the shadow lights view + // you can specify what your output should be + // @param name - this tells jshader's of child passes (which receive the parents output as input) + // what the sampler2d uniform name will be for this output texture + // @param type - the type of output could be tex2d or target + // @param width - optional width of the render target + // @param height - optional height of the render target + // @param mips - optional flag indicating whether the render target will support mip-mapping + + // the geometry pass + 'name': "geoPass", + 'geometrySet': "ALL" + // 'outputs':[{ 'name':"u_mainRT", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // 'children': + // [ + // // shadow pass + // { + // 'outputs':[{ 'name':"u_shadowDepthMap", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // // custom parameter + // 'name':"shadowDepthMap", + // 'shader': rdgeDepthMapShaderDef, + // 'technique':"shadowDepthMap", + // 'geometrySet':"SHADOW", + // 'children': + // [ + // // create shadow rt + // { + // 'outputs':[{ 'name':"u_shadowMap", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // 'name':"shadowMap", + // 'shader': rdgeShadowMapShader, + // 'clearColor' : [1.0,1.0,1.0,1.0], + // 'geometrySet':"SHADOW", + // } + // ] + // }, + // // glow pass + // { + // 'outputs':[{ 'name':"sTexture", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // 'name':"glowMap", + // 'shader': rdgeGlowMapShader, + // 'clearColor' : [0.0,0.0,0.0,1.0], + // 'technique':"createGlowMap", + // 'geometrySet':"ALL", + // 'children': + // [ + // { // the blur pass at half resolution + // 'name':"blurQuarter", + // 'geometrySet':"SCREEN_QUAD", + // 'shader': rdgeGaussianBlurShader, + // 'outputs':[{ 'name':"sTexture", 'type':"target", 'width':256, 'height':256, 'mips':false }], + // 'children': + // [ + // { // the blur pass at half resolution + // 'name':"blurThreeQuarter", + // 'geometrySet':"SCREEN_QUAD", + // 'shader': rdgeGaussianBlurShader, + // 'outputs':[{ 'name':"sTexture", 'type':"target", 'width':128, 'height':128, 'mips':false }], + // 'children': + // [ + // { // the blur pass at quarter resolution + // 'name':"blurFull", + // 'geometrySet':"SCREEN_QUAD", + // 'shader': rdgeGaussianBlurShader, + // 'outputs':[{ 'name':"u_glowFinal", 'type':"target", 'width':1024, 'height':1024, 'mips':false }] + // } + // ] + // } + // ] + // } + // ] + // }, + // // depth map in view space + // { + // 'name':"depth_map", + // 'shader': rdgeDepthMapShaderDef, + // 'technique': "depthMap", + // 'outputs':[{ 'name':"u_depthMap", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // 'geometrySet':"ALL", + // 'children' : + // [ + // // get the normals in view space + // { + // 'name':"normals", + // 'shader': rdgeViewSpaceNormalsShader, + // 'outputs':[{ 'name':"u_normalsRT", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // 'geometrySet':"ALL", + // 'children' : + // [ + // // techniques requiring depth and normals in view space go here + // + // // SSAO map + // { + // 'name':"SSAO", + // 'shader': rdgeSSAOShader, + // 'outputs':[{ 'name':"u_ssaoRT", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], + // 'textures':[{ 'type':"tex2d", 'data':"u_depthMap" }], + // 'geometrySet':"SCREEN_QUAD", + // 'preRender': function() + // { + // this.shader.ssao.u_cameraFTR.set(this.renderer.cameraManager().getActiveCamera().getFTR()); + // } + // } + // ] + // } + // ] + // }, + // + // // final pass must always be last in the list + // { + // // this final pass has no output, its shader, however, will render its input (the previous pass's output) + // // to the screen-quad geometry setup under the 'renderList' object + // 'name':"finalPass", + // 'geometrySet':"SCREEN_QUAD", + // 'textures':[{ 'type':"tex2d", 'data':"u_glowFinal" }, { 'type':"tex2d", 'data':"u_ssaoRT" }, { 'type':"tex2d", 'data':"u_shadowMap" }], + // 'shader': rdgeScreenQuadShaderDef, + // } + // ] + }; + + // a graph of render passes to process in order to produce a final output + this.renderGraph = new jpassGraph(this.defaultPassDef); + //this.renderGraph.shadowDepthMap.camera = this.mainLight; + + this.animstack = []; + this.pushAnim = function( anim ) + { + this.animstack.push(anim); + } + + this.popAnim = function( anim ) + { + this.animstack.pop(); + } + + this.playAnim = function( anim ) + { + this.popAnim(); + this.pushAnim( anim ); + } + + this.stopAllAnims = function() + { + this.animstack = []; + } + + this.jdefShaderProgram = new jshader(); + this.jdefShaderProgram.def = rdgeDefaultShaderDefintion; + this.jdefShaderProgram.init(); + + //this.defaultRenderProc = new DefaultRender(); + + this.drawTag = document.getElementById('draw_count'); + + mapping = new Array(); + mapping.process = function(trNode, parent) + { + mapping[trNode.name] = trNode; + } + this.Traverse(mapping); + + this.findNode = function(name) + { + return mapping[name]; + } + + /* + * scene traversal functor for finding a node by name + */ + findNodeByName = function(nodeName) + { + this.result = null; + this.process = function(node, parent) + { + if(node.name == nodeName) + { + this.result = node; + } + return true; + } + + this.init = function(){ this.result = null; } + } + + /* + * scene traversal functor for creating a list of node with a given name + */ + buildNodeList = function(nodeName) + { + this.result = []; + this.process = function(node, parent) + { + if(node.name == nodeName) + { + this.result.push(node); + } + return true; + } + this.init = function(){ this.result = []; } + } + + /* + * scene traversal functor for creating a list of nodes based on a regular expression + */ + buildNodeListRegex = function( re ) + { + this.result = []; + this.process = function(node, parent) + { + if(re.test(node.name)) + { + this.result.push(node); + } + return true; + } + this.init = function(){ this.result = []; } + } + + /* + * scene traversal functor for importing a previously exported json scene + */ + importScene = function() + { + this.renderer = g_Engine.getContext().renderer; + + this.process = function(node, parent) + { + node.parent = parent; + + if(node.nodeType === rdgeConstants.nodeType.TRNODE) + { + node.transformNodeTemplate = undefined; + verifyTransformNode(node); + + if(node.materialNode) + { + node.materialNode.materialNodeTemplate = undefined; + verifyMaterialNode(node.materialNode); + + if(node.materialNode.shaderProgram) + { + var shader = new jshader(); + shader.def = JSON.parse(node.materialNode.shaderProgram); + shader.init(); + node.materialNode.shaderProgram = shader; + } + + var texList = node.materialNode.textureList; + for(var i = 0, len = texList.length; i < len; ++i) + { + texList[i].handle = this.renderer.getTextureByName(texList[i].handle.lookUpName, texList[i].handle.texparams.wrap, texList[i].handle.texparams.mips); + } + + var lights = node.materialNode.lightChannel; + for(var i = 0, len = lights.length; i < len; ++i) + { + if(lights[i]) + { + lights[i].lightNodeTemplate = undefined; + verifyLightNode(lights[i]); + } + } + } + + // load meshes into context + for(var i = 0, len = node.meshes.length; i < len; ++i) + { + var mesh = g_meshMan.getModelByName(node.meshes[i].mesh.name); +// mesh.primitive.built = false; + this.renderer.createPrimitive(mesh.primitive); + } + } + + return true; + } + + this.init = function(){ this.result = []; } + } + + /* + * helper comparison functions + */ + __compareLessThan = function(a, b) + { + return a < b; + } + __compareGreaterThan = function(a, b) + { + return a > b; + } + + /* + * scene traversal functor for creating a sorted list + */ + insertIntoSortedList = function(list, item, comparator) + { + // insert at the end + list.push(item); + + var len = list.length; + + // get the active camera + var cam = g_Engine.getContext().renderer.cameraManager().getActiveCamera(); + + // camera z plane + var look = [ cam.world[8], cam.world[9], cam.world[10] ]; + // to object vector + var toObject = [ cam.world[12] - item.node.world[12], cam.world[13] - item.node.world[13], cam.world[14] - item.node.world[14] ]; + + // get the distance from object to cameras' 'z' plane + item.depth = vec3.dot( look, toObject ); + + // walk down the list of object moving the current item into place until the comparison fails + var i = len - 1; + var temp = null; + for(; i > 0; --i) + { + if( comparator(item.depth, list[i - 1].depth) ) + { + temp = list[i - 1] + list[i - 1] = list[i]; + list[i] = temp; + } + else + { + break; + } + } + } + + /* + * Helper function to generate a culled list of geometry for shadow mapping from the mainLights point of view + */ + shadowCullPass = function( cameraLight, sortCat, compareFunc ) + { + this.result = []; + this.activeCam = cameraLight; + this.bucketType = sortCat; + this.compare = compareFunc; + + this.process = function(node, parent) + { + // test visibility + if( node.bbox_world && node.bbox_world.isValid() ) + { + if( !node.bbox_world.isVisible(this.activeCam.frustum) ) + { + return false; + } + } + + if(node.materialNode && node.materialNode.sortCategory == this.bucketType) + { + insertIntoSortedList(this.result, { 'context': new RenderContext(),'node': node,'parent': parent }, this.compare); + } + + return true; + } + this.init = function(){ this.result = []; } + } + + /* + * Helper function to create a list sorted front to back as would be used by an opaque render list + * note: this is an insertion sort designed to create a new sorted list, not sort an existing list (optimization) + */ + insertFrontToBack = function( list, item) + { + insertIntoSortedList(list, item, __compareLessThan); + } + + /* + * Helper function to create a list sorted back to front as would be used by an transparent render list + * note: this is an insertion sort designed to create a new sorted list, not sort an existing list (optimization) + */ + insertBackToFront = function(list, item) + { + insertIntoSortedList(list, item, __compareGreaterThan); + } + + // sort map + this.sortFunc = []; + this.sortFunc[this.bckTypes.BACKGROUND] = function(list, item){ list.push(item); }; + this.sortFunc[this.bckTypes.OPAQUE] = insertFrontToBack; + this.sortFunc[this.bckTypes.TRANSPARENT] = insertBackToFront; + this.sortFunc[this.bckTypes.ADDITIVE] = insertBackToFront; + this.sortFunc[this.bckTypes.TRANSLUCENT] = insertBackToFront; + this.sortFunc[this.bckTypes.FOREGROUND] = function(list, item){ list.push(item); }; +} + +/* + * functor must have 'process(node, parent)' function defined, it takes a scene transform node, and the parent + */ +SceneGraph.prototype.Traverse = function(functor, isDepthFirst) +{ + if(this.scene==null) + { + window.console.log("traversing a NULL scene!"); + return; + } + + if(functor.init) + functor.init(); + + if(isDepthFirst) + { + this._TraverseDFHelper(functor,this.scene); + } + else + { + this._TraverseBFHelper(functor,this.scene); + } +} + +/* + * adds a transform node under the root of the scene + */ +SceneGraph.prototype.addNode = function( trNode ) +{ + verifyTransformNode(trNode); + + this.scene.children.push({transformNode:trNode}); +} + +/* + * adds a transform node under the root of the scene + */ +SceneGraph.prototype.insertUnder = function( targetNode, newNode ) +{ + verifyTransformNode(targetNode); + + targetNode.insertAsChild(newNode); +} + +/* + * adds a transform node under the root of the scene + */ +SceneGraph.prototype.insertAbove = function( targetNode, newNode ) +{ + verifyTransformNode(targetNode); + + targetNode.insertAsParent(newNode); +} + +/* + * locates a node by name + * @return returns the node if found or null otherwise + */ +SceneGraph.prototype.getNode = function(nodeName) +{ + var functor = new findNodeByName(nodeName); + this.Traverse(functor, true); + + return functor.result; +} + +/* + * locates a node by name + * @return returns a list of all node with the name requested + */ +SceneGraph.prototype.getNodes = function(nodeName) +{ + var functor = new buildNodeList(nodeName); + this.Traverse(functor, true); + + return functor.result; +} + +/* + * locates a node by name + * @return returns a list of all node with the name requested + */ +SceneGraph.prototype.getNodesRegex = function( re ) +{ + if( typeof re == "string") + { + re = new RegExp(re); + } + + var functor = new buildNodeListRegex(re); + this.Traverse(functor, true); + + return functor.result; +} + +/* + * Depth first Traverse helper + */ +SceneGraph.prototype._TraverseDFHelper=function(functor,node,parent) +{ + if(node.children!='undefined') + { + var queue=[]; + + queue.push({ 'node': node,'parent': null }); + + while(queue.length>0) + { + // pop the head and process it + var trNode=queue.pop(); + var tr=trNode.node; + var parent=trNode.parent; + + if(tr.transformNode!==undefined) + { + tr=tr.transformNode; + if(functor.process(tr,parent) === false) + { + continue; + } + } + + if(tr.children===undefined) + continue; + + // push on kids + for(var kid=0;kid0) + { + + top = queue.length; + + var topNode = tr.children[ top - 1 ]; + + var len = topNode.children == undefined ? 0 : topNode.children.length; + + // push on children + for(var child=0; child < len; ++child) { + queue.push({ 'node': tr.children[child],'parent': tr}); + } + + // did we push anything on? + if(len > 0 ) + continue; + + // pop the head and process it + var trNode=queue.pop(); + var tr=trNode.node; + var parent=trNode.parent; + + if(tr.transformNode!==undefined) { + tr=tr.transformNode; + functor.process(tr,parent); + } + } + } +} + +/* + * Depth first post process Traverse helper + */ +SceneGraph.prototype.BuildBVHHelper=function(node) +{ + if(node.children!='undefined') + { + if (node.bbox_world) + node.bbox_world.reset(); + else + node.bbox_world = new box(); + + if(node.local == undefined) + { + node.local = mat4.identity(); + } + + var queue=[]; + + var idIndex = 0; + node.id = "root"; + queue.push({ 'node': node, 'xfrm' : mat4.identity(), 'parent': null, 'visited':false}); + + var top = queue.length; + var topIndex = 0; + + while(top>0) + { + // update the top + top = queue.length; + topIndex = top - 1; + + var curNode = queue[ topIndex ].node.transformNode == undefined ? queue[ topIndex ].node : queue[ topIndex ].node.transformNode; + var parentXfrm = queue[ topIndex ].xfrm; + var parent = queue[ topIndex ].parent; + var visited = queue[ topIndex ].visited; + if(curNode.id == undefined) curNode.id = "id"+idIndex; + + if(!visited) + { + // Copy the parent's world mat and setup bounding box + if (curNode.local !== undefined) + { + + if (curNode.bbox_world) + curNode.bbox_world.reset(); + else + curNode.bbox_world = new box(); + + var bbox = this.GetBBoxForNode(curNode); + + // transform child node by parent + curNode.world = mat4.mul(curNode.local, parentXfrm); + + if(bbox) + { + // update bounding box position + curNode.bbox_world = bbox.transform(curNode.world); + } + + // make sure the nodes have a bounding volume so they dont impede the propagation + if( !bbox || !bbox.isValid()) + { + var dummybb = new box(); + dummybb.set(0,0); + curNode.bbox_world = dummybb; + } + + } + + // child count + var len = curNode.children == undefined ? 0 : curNode.children.length; + + // push on children + for(var child=0; child < len; ++child) + { + queue.push({ 'node': curNode.children[child],'xfrm':curNode.world, 'parent': curNode, 'visited':false}); + } + + idIndex++; + + // did we push anything on if so then this is not a leaf and we dont pop? + if(len > 0 ) + continue; + } + + // propagate the bounding volume up the hierarchy + if( parent && parent.bbox_world && parent.bbox_world.isValid() + && curNode.bbox_world && curNode.bbox_world.isValid()) + { + parent.bbox_world.addBox(curNode.bbox_world); + } + + // remove top node + queue.pop(); + + // update the top + top = queue.length; + + if(top > 0) + { + // if the previous node in the stack/queue was the parent of this node, then mark the parent as visited + var prevNode = queue[ top - 1 ].node.transformNode == undefined ? queue[ top - 1 ].node : queue[ top - 1 ].node.transformNode; + + if(prevNode.id == parent.id) + queue[ top - 1 ].visited = true; + } + } + } +} + + +/* + * Breadth first Traverse helper + */ +SceneGraph.prototype._TraverseBFHelper = function(functor, node) { + + if (node.children != 'undefined') { + var queue = []; + + queue.push( { 'node': node, 'parent' : null } ); + + while (queue.length > 0) { + // pop the head and process it + var trNode = queue.shift(); + var tr = trNode.node; + var parent = trNode.parent; + + if (tr.transformNode !== undefined) { + tr = tr.transformNode; + functor.process(tr, parent); + } + + if (tr.children === undefined) + continue; + + // push on kids + for (var kid = 0; kid < tr.children.length; ++kid) { + queue.push( { 'node' : tr.children[kid], 'parent' : tr }); + } + } + } +} + +/* +* Update the scene +*/ +SceneGraph.prototype.update = function(dt) +{ + var renderer = g_Engine.getContext().renderer; + g_Engine.getContext().debug.mat4CallCount = 0; + + // animation update... + var i = this.animstack.length - 1; + while( i >= 0 ) { + this.animstack[i].step(dt); + --i; + } + + this.BuildBVHHelper(this.scene); + + g_particleSystemManager.update(dt); + + // Set camera transform + if(!g_enableFlyCam) + { + var activeCam = renderer.cameraManager().getActiveCamera(); + + if(activeCam.controller != null && activeCam.controller.world != undefined) + { + activeCam.setWorld( activeCam.controller.world ); + } + + // Set camera transform + if(!g_enableFlyCam) + { + var activeCam = renderer.cameraManager().getActiveCamera(); + + this.tick++; + } + + } + + this.tick++; +} + +/* + * Render the scene + */ +SceneGraph.prototype.render = function(renderProc, forceThisProc) +{ + if(this.scene.children.length == 0) + return; + + var renderer = g_Engine.getContext().renderer; + + g_drawCount = 0; + + rdgeGlobalParameters.u_shadowLightFarZ.set([this.mainLight.zFar()]); + + this.renderList = this._RenderDFHelper(renderer, renderProc, this.scene, forceThisProc); + + if(this.shadowsEnabled) + { + this.Traverse(this.shadowCuller, true); + this.shadowRenderList = this.shadowCuller.result; + } + + this.renderGraph.render(this); + + g_particleSystemManager.render(); + + if(this.drawTag) + this.drawTag.innerHTML = 'Draws/frame: ' + g_drawCount + '| mat4 bind: ' + g_Engine.getContext().debug.mat4CallCount + ''; + +} + +/* + * Returns the bbox for the passed node, if present + */ +SceneGraph.prototype.GetBBoxForNode = function(tr) +{ + var bbox = null; + + if (tr.materialNode && tr.materialNode.meshNode) + { + var mesh = tr.materialNode.meshNode.mesh; + var model = null; + + model = g_meshMan.getModelByName(mesh.name); + + if (model != null) + bbox = model.bbox; + } + + return bbox; + } + + /* + * Depth first Traverse Render helper + */ + +SceneGraph.prototype._RenderDFHelper = function(renderer, renderProc, node, forceThisProc) +{ + renderList = []; + renderList[this.bckTypes.BACKGROUND] =[]; //BACKGROUND + renderList[this.bckTypes.OPAQUE] =[]; //OPAQUE + renderList[this.bckTypes.TRANSPARENT] =[]; //TRANSPARENT + renderList[this.bckTypes.ADDITIVE] =[]; //ADDITIVE + renderList[this.bckTypes.TRANSLUCENT] =[]; //TRANSLUCENT + renderList[this.bckTypes.FOREGROUND] =[]; //FOREGROUND + + if (node.children != 'undefined') + { + var queue_bf = []; + + // + // Render Pass + // + // the context - default settings + var _Ctx = g_Engine.defaultContext; + + // last depth in tree + var lastAppliedID = 0; + + var isRoot = true, isVisible = true, contextDirty = true; + + queue_bf.push({ 'node': node, 'curCtx': _Ctx }); + + var activeCam = renderer.cameraManager().getActiveCamera(); + + while (queue_bf.length > 0) + { + + isVisible = true; + + // pop the head and process it + var trNode = queue_bf.pop(); + var tr = trNode.node; + + + // default context + var curCtx = new RenderContext(); + curCtx.Load(trNode.curCtx); + + // flatten out matrices gather render context from material and render + if(tr.transformNode!==undefined) + { + + tr=tr.transformNode; // if transform exist we need to use it as our current node to check for kids + + if(tr.hide!==undefined&&tr.hide==true) + { + continue; + } + + g_sceneStats.nodesVisited.value++; + + // test visibility + if(this.frustumCulling && tr.bbox_world && tr.bbox_world.isValid()) + { + if(!tr.bbox_world.isVisible(activeCam.frustum)) + { + g_sceneStats.nodesCulled.value++; + continue; + } + } + g_sceneStats.nodesVisible.value++; + + curCtx.shaderProg= this.jdefShaderProgram; + + var renderBucket = rdgeConstants.categoryEnumeration.OPAQUE; + + // get material if its available + if(tr.materialNode) + { + renderBucket = tr.materialNode.sortCategory; + + // set shader to use + if(tr.materialNode.shaderProgram !== undefined) + { + curCtx.shaderProg=tr.materialNode.shaderProgram; // use whats on the node if we are not beinged forced + } + + if(tr.materialNode.textureList!==undefined) + { + curCtx.textureList = tr.materialNode.textureList.slice(); + } + + if(tr.materialNode.uniforms.length>0) + { + curCtx.uniforms=tr.materialNode.uniforms.slice(); + } + + var len = tr.materialNode.lightChannel.length; + for(var i = 0; i < len; ++i) + { + if(tr.materialNode.lightChannel[i]) + curCtx.lights[i] = tr.materialNode.lightChannel[i]; + } + } + + // push onto deferred render list + this.sortFunc[renderBucket](renderList[renderBucket], { 'context': curCtx,'node': tr,'parent': parent }); + + g_drawCount++; + } + + if (tr.children === undefined) + continue; + + // push on kids + var numKids = tr.children.length; + for (var kid = 0; kid < numKids; ++kid) + { + queue_bf.push({ 'node': tr.children[kid], 'curCtx': curCtx }); + } + + } + } + + return renderList; +} + +SceneGraph.prototype.enableShadows = function( areEnabled ) +{ + if( areEnabled ) + { + var renderer = g_Engine.getContext().renderer; + + this.shadowCuller = new shadowCullPass(this.mainLight, rdgeConstants.categoryEnumeration.OPAQUE, function(a, b) { return a < b; }); + + this.mainLight.init(); + // lights position and point of view + this.mainLight.setPerspective(45.0, renderer.vpWidth/renderer.vpHeight, 1.0, 200.0); + this.mainLight.setLookAt([-60,17,-15],[-5,-5,15],vec3.up()); + + // setup light params + rdgeGlobalParameters.u_shadowLightWorld.set( this.mainLight.world ); + rdgeGlobalParameters.u_vShadowLight.set( this.mainLight.view ); + var shadowMatrix = mat4.identity(); + shadowMatrix = mat4.scale( shadowMatrix, [0.5, 0.5, 0.5] ); + shadowMatrix = mat4.translate( shadowMatrix, [0.5, 0.5, 0.5] ); + rdgeGlobalParameters.u_shadowBiasMatrix.set( shadowMatrix ); + var BiasProjViewMat = mat4.mul( this.mainLight.proj, shadowMatrix ); + BiasProjViewMat = mat4.mul( this.mainLight.view, BiasProjViewMat ); + rdgeGlobalParameters.u_shadowBPV.set( BiasProjViewMat ); + + this.shadowsEnabled = true; + } + else + { + this.mainLight = null; + this.shadowsEnabled = false; + } +} + + +SceneGraph.prototype.exportJSON = function() +{ + objMap = []; + + function replacer(key, value) + { + if(key == 'bbox_world') + { + return null; + } +// else if(key === 'image') +// { +// return "image"; +// } + else if(key === 'parent') + { + return "parent"; + } + +// for(var i = 0, len = objMap.length; i < len; ++i) +// { +// if((value && typeof value === "object" && !value.jsonExportName && value === objMap[i]) || (value && value.baseURI !== undefined)) +// { +// return 'replaced'; +// } +// } +// +// if(value && value.baseURI === undefined && typeof value === "object" && !value.lookUpName) +// objMap.push(value); + + return value; + } + + var val = {'scene':null, 'meshes':null}; + val.scene = JSON.stringify(this.scene, replacer); + + val.meshes = g_meshMan.exportJSON(); + + val = JSON.stringify(val); + + return val; +} + +SceneGraph.prototype.importJSON = function ( jsonScene ) +{ + try + { + if(jsonScene) + { + var sceneImport = JSON.parse(jsonScene); + + if(sceneImport) + { + this.scene = JSON.parse(sceneImport.scene); + + if(sceneImport.meshes) + { + g_meshMan.importJSON(sceneImport.meshes); + } + + if(this.scene) + { + // traverse the scene, re-creating missing components + var importer = new importScene(); + this.Traverse(importer, true); + + window.console.log("scene imported"); + } + } + } + }catch(e) + { + window.console.error("error importing JSON scene: " + e.description); + } + +} \ No newline at end of file -- cgit v1.2.3