/* <copyright> This file contains proprietary software owned by Motorola Mobility, Inc.<br/> No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. </copyright> */ // RDGE namespaces var RDGE = RDGE || {}; // default render proc // render scene nodes RDGE.DefaultRender = function () { // setup the default shader this.renderer = RDGE.globals.engine.getContext().renderer; //this.shaderProgram = this.renderer.defaultShader; this.jshaderProgram = new RDGE.jshader(); this.jshaderProgram.def = this.renderer.defaultShaderDefintion; this.jshaderProgram.init(); }; RDGE.DefaultRender.prototype.process = function (context, trNode, parent) { }; function renderObject(trNode, renderCtx, parent) { this.node = trNode ? trNode : RDGE.createTransformNode(); this.context = renderCtx ? renderCtx : new RDGE.RenderContext(); this.parent = parent ? parent : null; }; // Scene graph contains set of tools for manipulating the graph RDGE.SceneGraph = function (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 RDGE.shadowLight(); this.bckTypes = RDGE.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': RDGE.rdgeDepthMapShaderDef, // 'technique':"shadowDepthMap", // 'geometrySet':"SHADOW", // 'children': // [ // // create shadow rt // { // 'outputs':[{ 'name':"u_shadowMap", 'type':"target", 'width':1024, 'height':1024, 'mips':false }], // 'name':"shadowMap", // 'shader': RDGE.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': RDGE.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': RDGE.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': RDGE.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': RDGE.rdgeGaussianBlurShader, // 'outputs':[{ 'name':"u_glowFinal", 'type':"target", 'width':1024, 'height':1024, 'mips':false }] // } // ] // } // ] // } // ] // }, // // depth map in view space // { // 'name':"depth_map", // 'shader': RDGE.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': RDGE.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': RDGE.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': RDGE.rdgeScreenQuadShaderDef, // } // ] }; // a graph of render passes to process in order to produce a final output this.renderGraph = new RDGE.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 RDGE.jshader(); this.jdefShaderProgram.def = RDGE.rdgeDefaultShaderDefintion; this.jdefShaderProgram.init(); //this.defaultRenderProc = new RDGE.DefaultRender(); 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 = RDGE.globals.engine.getContext().renderer; this.process = function (node, parent) { node.parent = parent; if (node.nodeType === RDGE.rdgeConstants.nodeType.TRNODE) { node.transformNodeTemplate = undefined; RDGE.verifyTransformNode(node); if (node.materialNode) { node.materialNode.materialNodeTemplate = undefined; RDGE.verifyMaterialNode(node.materialNode); if (node.materialNode.shaderProgram) { var shader = new RDGE.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; RDGE.verifyLightNode(lights[i]); } } } // load meshes into context for (var i = 0, len = node.meshes.length; i < len; ++i) { var mesh = RDGE.globals.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 = RDGE.globals.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 = RDGE.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 RDGE.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 */ RDGE.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 */ RDGE.SceneGraph.prototype.addNode = function (trNode) { RDGE.verifyTransformNode(trNode); this.scene.children.push({ transformNode: trNode }); }; /* * adds a transform node under the root of the scene */ RDGE.SceneGraph.prototype.insertUnder = function (targetNode, newNode) { RDGE.verifyTransformNode(targetNode); targetNode.insertAsChild(newNode); }; /* * adds a transform node under the root of the scene */ RDGE.SceneGraph.prototype.insertAbove = function (targetNode, newNode) { RDGE.verifyTransformNode(targetNode); targetNode.insertAsParent(newNode); }; /* * locates a node by name * @return returns the node if found or null otherwise */ RDGE.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 */ RDGE.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 */ RDGE.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 */ RDGE.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; kid < tr.children.length; ++kid) { queue.push({ 'node': tr.children[kid], 'parent': tr }); } } } }; /* * Depth first post process Traverse helper */ RDGE.SceneGraph.prototype._TraverseDFPostOrderHelper = function (functor, node, parent) { if (node.children != 'undefined') { var queue = []; queue.push({ 'node': node, 'parent': null }); var top = queue.length; while (top > 0) { 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 */ RDGE.SceneGraph.prototype.BuildBVHHelper = function (node) { if (node.children != 'undefined') { if (node.bbox_world) node.bbox_world.reset(); else node.bbox_world = new RDGE.box(); if (node.local == undefined) { node.local = RDGE.mat4.identity(); } var queue = []; var idIndex = 0; node.id = "root"; queue.push({ 'node': node, 'xfrm': RDGE.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 RDGE.box(); var bbox = this.GetBBoxForNode(curNode); // transform child node by parent curNode.world = RDGE.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 RDGE.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 */ RDGE.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 */ RDGE.SceneGraph.prototype.update = function (dt) { var renderer = RDGE.globals.engine.getContext().renderer; RDGE.globals.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); RDGE.g_particleSystemManager.update(dt); var activeCam = renderer.cameraManager().getActiveCamera(); if (activeCam !== undefined && activeCam != null && activeCam.controller != null && activeCam.controller.world !== undefined) { activeCam.setWorld(activeCam.controller.world); } this.tick++; }; /* * Render the scene */ RDGE.SceneGraph.prototype.render = function (renderProc, forceThisProc) { if (this.scene.children.length == 0) return; var renderer = RDGE.globals.engine.getContext().renderer; RDGE.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); RDGE.g_particleSystemManager.render(); }; /* * Returns the bbox for the passed node, if present */ RDGE.SceneGraph.prototype.GetBBoxForNode = function (tr) { var bbox = null; if (tr.materialNode && tr.materialNode.meshNode) { var mesh = tr.materialNode.meshNode.mesh; var model = null; model = RDGE.globals.meshMan.getModelByName(mesh.name); if (model != null) bbox = model.bbox; } return bbox; }; /* * Depth first Traverse Render helper */ RDGE.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 = RDGE.globals.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 RDGE.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; } // test visibility if (this.frustumCulling && tr.bbox_world && tr.bbox_world.isValid()) { if (!tr.bbox_world.isVisible(activeCam.frustum)) { continue; } } curCtx.shaderProg = this.jdefShaderProgram; var renderBucket = RDGE.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 }); } 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; }; RDGE.SceneGraph.prototype.enableShadows = function (areEnabled) { if (areEnabled) { var renderer = RDGE.globals.engine.getContext().renderer; this.shadowCuller = new shadowCullPass(this.mainLight, RDGE.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], RDGE.vec3.up()); // setup light params RDGE.rdgeGlobalParameters.u_shadowLightWorld.set(this.mainLight.world); RDGE.rdgeGlobalParameters.u_vShadowLight.set(this.mainLight.view); var shadowMatrix = RDGE.mat4.identity(); shadowMatrix = RDGE.mat4.scale(shadowMatrix, [0.5, 0.5, 0.5]); shadowMatrix = RDGE.mat4.translate(shadowMatrix, [0.5, 0.5, 0.5]); RDGE.rdgeGlobalParameters.u_shadowBiasMatrix.set(shadowMatrix); var BiasProjViewMat = RDGE.mat4.mul(this.mainLight.proj, shadowMatrix); BiasProjViewMat = RDGE.mat4.mul(this.mainLight.view, BiasProjViewMat); RDGE.rdgeGlobalParameters.u_shadowBPV.set(BiasProjViewMat); this.shadowsEnabled = true; } else { this.mainLight = null; this.shadowsEnabled = false; } }; RDGE.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 = RDGE.globals.meshMan.exportJSON(); val = JSON.stringify(val); return val; }; RDGE.SceneGraph.prototype.importJSON = function (jsonScene) { try { if (jsonScene) { var sceneImport = JSON.parse(jsonScene); if (sceneImport) { this.scene = JSON.parse(sceneImport.scene); if (sceneImport.meshes) { RDGE.globals.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); } };