/* 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.
*/ // globals var shaderProgramArray = new Array; var glContextArray = new Array; var vertexShaderSource = ""; var fragmentShaderSource = ""; var rdgeStarted = false; var nodeCounter = 0; /////////////////////////////////////////////////////////////////////// // Class GLWorld // Manages display in a canvas /////////////////////////////////////////////////////////////////////// function GLWorld( canvas, use3D ) { /////////////////////////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////////////////////////// // flag to do the drawing with WebGL this._useWebGL = false; if(use3D) this._useWebGL = use3D; this._canvas = canvas; if (this._useWebGL) this._glContext = canvas.getContext("experimental-webgl"); else this._2DContext = canvas.getContext( "2d" ); this._viewportWidth = canvas.width; this._viewportHeight = canvas.height; // view parameters this._fov = 45.0; this._zNear = 0.1; this._zFar = 100.0; this._viewDist = 5.0; // default light parameters this._ambientLightColor = [0.1, 0.1, 0.1, 1.0]; this._diffuseLightColor = [0.1, 0.1, 0.1, 1.0]; this._specularLightColor = [0.6, 0.6, 0.6, 1.0]; this._pointLightLoc = [0.0, 0.0, 0.05]; // default material properties. Material properties should be overridden // by the materials used by the objects this._materialShininess = 20.0; this._geomRoot = undefined; this._cameraMat = Matrix.I(4); this._cameraMat[14] = 5.0; this._cameraMatInv = Matrix.I(4); this._cameraMatInv[14] = -5.0; this._camera; /////////////////////////////////////////////////////////////////////// // Property accessors /////////////////////////////////////////////////////////////////////// this.getGLContext = function() { return this._glContext; } this.setGLContext = function(gl) { this._glContext = gl; } this.get2DContext = function() { return this._2DContext; } this.set2DContext = function(c) { this._2DContext = c; } this.getCanvas = function() { return this._canvas; } this.setCanvas = function(c) { this._canvas = c; } this.getShaderProgram = function() { return this._shaderProgram; } this.getViewportWidth = function() { return this._viewportWidth; } this.getViewportHeight = function() { return this._viewportHeight; } this.getAspect = function() { return this._viewportWidth/this._viewportHeight; } this.getGeomRoot = function() { return this._geomRoot; } this.getZNear = function() { return this._zNear; } this.getZFar = function() { return this._zFar; } this.getFOV = function() { return this._fov; } this.getCamera = function() { return this._camera; } this.getCameraMat = function() { return this._cameraMat.slice(0); } this.setCameraMat = function(c) { this._cameraMat = c.slice(0); this._cameraMatInv = glmat4.inverse(c, []); } this.getCameraMatInverse = function() { return this._cameraMatInv.slice(0); } this.getViewDistance = function() { return this._viewDist; } this.getRootNode = function() { return this._rootNode; } this.setRootNode = function(r) { this._rootNode = r; } this.isWebGL = function() { return this._useWebGL; } //////////////////////////////////////////////////////////////////////////////////// // RDGE // local variables this.myScene = null; this.elapsed = 0; this.light = null; this.light2 = null; this.fillShader = null; this.strokeShader = null; this.renderer = null; // this is the node to which objects get hung this._rootNode; // set up the camera matrix var camMat = Matrix.I(4); camMat[14] = this.getViewDistance(); this.setCameraMat( camMat ); ////////////////////////////////////////// // test call to SVG importer // console.log( "***** SVG TEST *****" ); // var svgImporter = new SVGParse( this ); // svgImporter.importSVG(); ////////////////////////////////////////// // post-load processing of the scene this.init = function() { var ctx1 = g_Engine.ctxMan.handleToObject(this._canvas.rdgeCtxHandle), ctx2 = g_Engine.getContext(); if (ctx1 != ctx2) console.log( "***** different contexts *****" ); this.renderer = ctx1.renderer; // create a camera, set its perspective, and then point it at the origin var cam = new camera(); this._camera = cam; cam.setPerspective(this.getFOV(), this.getAspect(), this.getZNear(), this.getZFar()); cam.setLookAt([0, 0, this.getViewDistance()], [0, 0, 0], vec3.up()); // make this camera the active camera this.renderer.cameraManager().setActiveCamera(cam); // change clear color this.renderer.setClearFlags(g_Engine.getContext().DEPTH_BUFFER_BIT); this.renderer.setClearColor([1.0, 1.0, 1.0, 0.0]); //this.renderer.NinjaWorld = this; // create an empty scene graph this.myScene = new SceneGraph(); // create some lights // light 1 this.light = createLightNode("myLight"); this.light.setPosition([0,0,1.2]); this.light.setDiffuseColor([0.75,0.9,1.0,1.0]); // light 2 this.light2 = createLightNode("myLight2"); this.light2.setPosition([-0.5,0,1.2]); this.light2.setDiffuseColor([1.0,0.9,0.75,1.0]); // create a light transform var lightTr = createTransformNode("lightTr"); // create and attach a material - materials hold the light data lightTr.attachMaterial(createMaterialNode("lights")); // enable light channels 1, 2 - channel 0 is used by the default shader lightTr.materialNode.enableLightChannel(1, this.light); lightTr.materialNode.enableLightChannel(2, this.light2); // all added objects are parented to the light node this._rootNode = lightTr; // add the light node to the scene this.myScene.addNode(lightTr); // Add the scene to the engine - necessary if you want the engine to draw for you g_Engine.AddScene("myScene" + this._canvas.id, this.myScene); } // main code for handling user interaction and updating the scene this.update = function(dt) { if (!dt) dt = 0.2; this.elapsed += dt; if (this._useWebGL) { // changed the global position uniform of light 0, another way to change behavior of a light rdgeGlobalParameters.u_light0Pos.set( [5*Math.cos(this.elapsed), 5*Math.sin(this.elapsed), 20]); // orbit the light nodes around the boxes this.light.setPosition([1.2*Math.cos(this.elapsed*2.0), 1.2*Math.sin(this.elapsed*2.0), 1.2*Math.cos(this.elapsed*2.0)]); this.light2.setPosition([-1.2*Math.cos(this.elapsed*2.0), 1.2*Math.sin(this.elapsed*2.0), -1.2*Math.cos(this.elapsed)]); } this.updateMaterials( this.getGeomRoot(), this.elapsed ); // now update all the nodes in the scene if (this._useWebGL) this.myScene.update(dt); } // defining the draw function to control how the scene is rendered this.draw = function() { if (this._useWebGL) { var ctx = g_Engine.getContext(); //console.log( "RDGE state: " + ctx.ctxStateManager.currentState().name); var renderer = ctx.renderer; renderer.disableCulling(); this.myScene.render(); } else { this.render(); } } // END RDGE //////////////////////////////////////////////////////////////////////////////////// // start RDGE passing your runtime object, and false to indicate we don't need a an initialization state // in the case of a procedurally built scene an init state is not needed for loading data //if (this._useWebGL) { if (this._useWebGL) { rdgeStarted = true; // TODO - temporary fix for RDGE id's this._canvas.id = this._canvas.uuid; g_Engine.registerCanvas(this._canvas, this); RDGEStart( this._canvas ); //this._canvas.fpsTracker = new fpsTracker( '0' ); //this._canvas.task = new RDGETask(this._canvas, true); //this._canvas.task.stop() //this._canvas.task.start() } } } /////////////////////////////////////////////////////////////////////// // Property Accessors /////////////////////////////////////////////////////////////////////// GLWorld.prototype.getGeomRoot = function() { return this._geomRoot; } /////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////// GLWorld.prototype.updateObject = function (obj) { if (!this._useWebGL) return; var prims = obj.getPrimitiveArray(); var materialNodes = obj.getMaterialNodeArray(); if (prims.length != materialNodes.length) throw new Error("inconsistent material and primitive counts"); var nPrims = prims.length; var ctrTrNode; if (nPrims > 0) { ctrTrNode = obj.getTransformNode(); if (ctrTrNode == null) { ctrTrNode = createTransformNode("objRootNode_" + nodeCounter++); this._rootNode.insertAsChild( ctrTrNode ); obj.setTransformNode( ctrTrNode ); } ctrTrNode.meshes.forEach(function(thisMesh) { g_meshMan.deleteMesh(thisMesh.mesh.name); }); ctrTrNode.meshes = []; ctrTrNode.attachMeshNode(this.renderer.id + "_prim_" + nodeCounter++, prims[0]); ctrTrNode.attachMaterial(materialNodes[0]); } var children = ctrTrNode.children; for (var i = 1; i < nPrims; i++) { // get the next primitive var prim = prims[i]; // get a previously created transform node. If the transform has not been created, create it var childTrNode; if (children && children.length >= i) { childTrNode = children[i-1].transformNode; childTrNode.meshes.forEach(function(thisMesh) { g_meshMan.deleteMesh(thisMesh.mesh.name); }); childTrNode.meshes = []; } else { childTrNode = createTransformNode("objNode_" + nodeCounter++); ctrTrNode.insertAsChild(childTrNode); } // attach the instanced box goe childTrNode.attachMeshNode(this.renderer.id + "_prim_" + nodeCounter++, prim); childTrNode.attachMaterial(materialNodes[i]); } } GLWorld.prototype.addObject = function( obj ) { if (!obj) return; try { // undefine all the links of the object obj.setChild( undefined ); obj.setNext( undefined ); obj.setPrev( undefined ); obj.setParent( undefined ); obj.setWorld( this ); // build the WebGL buffers if (this._useWebGL) obj.buildBuffers(); if (this._geomRoot == null) { this._geomRoot = obj; } else { var go = this._geomRoot; while (go.getNext()) go = go.getNext(); go.setNext( obj ); obj.setPrev( go ); } } catch(e) { alert( "Exception in GLWorld.addObject " + e ); } } //append to the list of objects if obj doesn't already exist //if obj exists, then don't add to list of objects GLWorld.prototype.addIfNewObject = function (obj) { if (!obj) return; try { obj.setWorld(this); // build the WebGL buffers if (this._useWebGL) obj.buildBuffers(); if (this._geomRoot == null) { this._geomRoot = obj; } else if (this._geomRoot !== obj) { var go = this._geomRoot; while (go.getNext() && go.getNext() !== obj) { go = go.getNext(); } if (go.getNext() === null) { // undefine all the links of the object obj.setChild(undefined); obj.setNext(undefined); obj.setPrev(undefined); obj.setParent(undefined); go.setNext(obj); obj.setPrev(go); } } } catch (e) { alert("Exception in GLWorld.addIfNewObject " + e); } } GLWorld.prototype.clearTree = function() { if (this._useWebGL) { var root = this._rootNode; root.children = new Array(); g_Engine.unregisterCanvas( this._canvas.id ) this.update( 0 ); this.draw(); } } GLWorld.prototype.updateMaterials = function( obj, time ) { if (!obj) return; var matArray = obj.getMaterialArray(); if (matArray) { var n = matArray.length; for (var i=0; i 0)) mat = this._matStack[ this._matStack.length-1]; return mat; } GLWorld.prototype.popMatrix = function() { if (this._matStack.length == 0) throw "Invalid popMatrix!"; var mat = this._matStack.pop(); return mat; } GLWorld.prototype.setMVMatrix = function() { var mat = this.stackTop(); if (mat) { var gl = this._glContext; //var mvMatrix = this._cameraMatInv.multiply(mat); var mvMatrix = glmat4.multiply( this._cameraMatInv, mat, []); //var mat2 = mat.multiply( this._cameraMatInv ); gl.uniformMatrix4fv(this._shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix)); var normalMatrix = mat3.create(); //mat4.toInverseMat3(mvMatrix, normalMatrix); // mat4.toInverseMat3(new Float32Array(mvMatrix.flatten()), normalMatrix); mat4.toInverseMat3(new Float32Array(mvMatrix), normalMatrix); mat3.transpose(normalMatrix); gl.uniformMatrix3fv(this._shaderProgram.nMatrixUniform, false, normalMatrix); } } GLWorld.prototype.makePerspectiveMatrix = function() { return makePerspective( this.getFOV(), this.getAspect(), this.getZNear(), this.getZFar() ); } function perspective(fovy, aspect, znear, zfar) { return makePerspective(fovy, aspect, znear, zfar); } GLWorld.prototype.render = function() { if (!this._useWebGL) { // clear the context var ctx = this.get2DContext(); if (!ctx) return; ctx.clearRect(0, 0, this.getViewportWidth(), this.getViewportHeight()); // render the geometry var root = this.getGeomRoot(); this.hRender( root ); } else { this.draw(); } } GLWorld.prototype.hRender = function( obj ) { if (!obj) return; obj.render(); this.hRender( obj.getChild() ); this.hRender( obj.getNext() ); } GLWorld.prototype.setViewportFromCanvas = function(canvas) { this._viewportWidth = canvas.width; this._viewportHeight = canvas.height; if (this._useWebGL) { this._glContext.viewportWidth = canvas.width; this._glContext.viewportHeight = canvas.height; this.getCamera().setPerspective(this.getFOV(), this.getAspect(), this.getZNear(), this.getZFar()); this.renderer.setViewPort(0, 0, canvas.width, canvas.height); } } GLWorld.prototype.getShapeFromPoint = function( offsetX, offsetY ) { var x = offsetX/this._canvas.width; var y = offsetY/this._canvas.height; var go = this._geomRoot; if(go.collidesWithPoint(x,y)) { console.log("collision found"); return go; } while (go.getNext()) { go = go.getNext(); if(go.collidesWithPoint(x,y)) { console.log("collision found"); return go; } } } GLWorld.prototype.export = function() { var exportStr = "GLWorld 1.0\n"; exportStr += "id: " + this._canvas.id + "\n"; exportStr += "fov: " + this._fov + "\n"; exportStr += "zNear: " + this._zNear + "\n"; exportStr += "zFar: " + this._zFar + "\n"; exportStr += "viewDist: " + this._viewDist + "\n"; // we need 2 export modes: One for save/restore, one for publish. // hardcoding for now var exportForPublish = false; exportStr += "publish: " + exportForPublish + "\n"; if (exportForPublish) { exportStr += "scenedata: " + this.myScene.exportJSON() + "endscene\n"; } else { // output the material library exportStr += MaterialsLibrary.export(); // write out all of the objects exportStr += "tree\n"; exportStr += this.exportObjects( this._geomRoot ); exportStr += "endtree\n"; } return exportStr; } GLWorld.prototype.exportObjects = function( obj ) { if (!obj) return; var rtnStr = "OBJECT\n"; rtnStr += obj.export(); if (obj.getChild()) rtnStr += this.exportObjects( obj.getChild () ); // the end object goes outside the children rtnStr += "ENDOBJECT\n"; if (obj.getNext()) rtnStr += this.exportObjects( obj.getNext() ); return rtnStr; } GLWorld.prototype.findTransformNodeByMaterial = function( materialNode, trNode ) { //if (trNode == null) trNode = this._ctrNode; if (trNode == null) trNode = this._rootNode; if ( trNode.transformNode && (materialNode == trNode.transformNode.materialNode)) return trNode; var rtnNode; if (trNode.children != null) { var nKids = trNode.children.length; for (var i=0; i= 0) { var rdgeStr = importStr.substr( index+11 ); var endIndex = rdgeStr.indexOf( "endscene\n" ); if (endIndex < 0) throw new Error( "ill-formed WebGL data" ); var len = endIndex - index + 11; rdgeStr = rdgeStr.substr( 0, endIndex ); this.myScene.importJSON( rdgeStr ); } else { // load the material library importStr = MaterialsLibrary.import( importStr ); // import the objects this.importObjects( importStr, this._rootNode ); } } GLWorld.prototype.importObjects = function( importStr, parentNode ) { var index = importStr.indexOf( "OBJECT\n", 0 ); while (index >= 0) { // update the string to the current object importStr = importStr.substr( index+7 ); // read the next object this.importObject( importStr, parentNode ); // determine if we have children var endIndex = importStr.indexOf( "ENDOBJECT\n" ), childIndex = importStr.indexOf( "OBJECT\n" ); if (endIndex < 0) throw new Error( "ill-formed object data" ); if ((childIndex >= 0) && (childIndex < endIndex)) { importStr = importStr.substr( childIndex + 7 ); importStr = this.importObjects( importStr, node ); endIndex = importStr.indexOf( "ENDOBJECT\n" ) } // remove the string for the object(s) just created importStr = importStr.substr( endIndex ); // get the location of the next object index = importStr.indexOf( "OBJECT\n", endIndex ); } return importStr; } GLWorld.prototype.importObject = function( objStr, parentNode ) { var go = new GLGeomObj(); var type = Number( go.getPropertyFromString( "type: ", objStr ) ); var obj; switch (type) { case 1: obj = new GLRectangle(); obj.import( objStr ); break; case 2: // circle obj = new GLCircle(); obj.import( objStr ); break; case 3: // line - not implemented default: throw new Error( "Unrecognized object type: " + type ); break; } if (obj) this.addObject( obj ); } GLWorld.prototype.importSubObject = function( objStr, parentNode ) { // get the mesh text var i0 = objStr.indexOf( "mesh: " ), i1 = objStr.indexOf( "endMesh\n" ); if ((i0 < 0) || (i1 < 0)) throw new Error( "ill-formed sub object" ); i0 += 6; var meshText = objStr.substr( i0, i1 - i0 ); var meshObj = JSON.parse(meshText); // get the material text var i0 = objStr.indexOf( "material: " ), i1 = objStr.indexOf( "endMat\n" ); if ((i0 < 0) || (i1 < 0)) throw new Error( "ill-formed sub object" ); i0 += 10; var matText = objStr.substr( i0, i1 - i0 ); var shaderDef = JSON.parse( matText ); var shader = new jshader(); shader.def = shaderDef; shader.init(); // set the shader for this material var matNode = createMaterialNode("objMat") matNode.setShader(shader); // create the transformation node var trNode = createTransformNode("subObjNode_" ); trNode.attachMeshNode(this.renderer.id + "_prim_", meshObj); trNode.attachMaterial(matNode); parentNode.insertAsChild(trNode); return trNode; }