/* <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> */ // namespace for the Ninja Canvas Runtime var NinjaCvsRt = NinjaCvsRt || {}; /////////////////////////////////////////////////////////////////////// //Loading webGL/canvas data NinjaCvsRt.initWebGl = function (rootElement, directory) { var cvsDataMngr, ninjaWebGlData = JSON.parse((document.querySelectorAll(['script[data-ninja-webgl]'])[0].innerHTML.replace('(', '')).replace(')', '')); if (ninjaWebGlData && ninjaWebGlData.data) { for (var n=0; ninjaWebGlData.data[n]; n++) { ninjaWebGlData.data[n] = unescape(ninjaWebGlData.data[n]); } } //Creating data manager cvsDataMngr = new NinjaCvsRt.CanvasDataManager(); //Loading data to canvas(es) cvsDataMngr.loadGLData(rootElement, ninjaWebGlData.data, directory); }; /////////////////////////////////////////////////////////////////////// // Class ShapeRuntime // Manages runtime shape display /////////////////////////////////////////////////////////////////////// NinjaCvsRt.CanvasDataManager = function () { this.loadGLData = function(root, valueArray, assetPath ) { if (assetPath) this._assetPath = assetPath.slice(); var value = valueArray; var nWorlds = value.length; for (var i=0; i<nWorlds; i++) { var importStr = value[i]; // there should be some version information in // the form of 'v0.0;' Pull that off. (the trailing ';' should // be in the first 24 characters). var index = importStr.indexOf( ';' ); if ((importStr[0] === 'v') && (index < 24)) { // JSON format. pull off the version info importStr = importStr.substr( index+1 ); var jObj = JSON.parse( importStr ); var id = jObj.id; if (id) { var canvas = this.findCanvasWithID( id, root ); if (canvas) { new NinjaCvsRt.GLRuntime( canvas, jObj, assetPath ); } } } } }; this.findCanvasWithID = function( id, elt ) { var cid = elt.getAttribute( "data-RDGE-id" ); if (cid == id) return elt; if (elt.children) { var nKids = elt.children.length; for (var i=0; i<nKids; i++) { var child = elt.children[i]; var foundElt = this.findCanvasWithID( id, child ); if (foundElt) return foundElt; } } }; }; /////////////////////////////////////////////////////////////////////// // Class GLRuntime // Manages runtime fora WebGL canvas /////////////////////////////////////////////////////////////////////// NinjaCvsRt.GLRuntime = function ( canvas, jObj, assetPath ) { /////////////////////////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////////////////////////// this._canvas = canvas; this._context = null; //this._importStr = importStr; this._jObj = jObj; this.renderer = null; this.myScene = null; this.light = null; this.light2 = null; this._rootNode = null; this._firstRender = true; this._initialized = false; this._useWebGL = false; this._assetPath = undefined; // view parameters this._fov = 45.0; this._zNear = 0.1; this._zFar = 100.0; this._viewDist = 5.0; this.elapsed = 0; this._aspect = canvas.width/canvas.height; //this._geomRoot = null; this._rootChildren = []; // all "live" materials this._materials = []; // provide the mapping for the asset directory if (assetPath) { this._assetPath = assetPath.slice(); if (this._assetPath[this._assetPath.length - 1] != '/') this._assetPath += '/'; } if(this._assetPath !== undefined) { RDGE.globals.engine.setAssetPath(this._assetPath); } /////////////////////////////////////////////////////////////////////// // accessors /////////////////////////////////////////////////////////////////////// this.getZNear = function() { return this._zNear; }; this.getZFar = function() { return this._zFar; }; this.getFOV = function() { return this._fov; }; this.getAspect = function() { return this._aspect; }; this.getViewDistance = function() { return this._viewDist; }; this.get2DContext = function() { return this._context; }; this.getViewportWidth = function() { return this._canvas.width; }; this.getViewportHeight = function() { return this._canvas.height; }; /////////////////////////////////////////////////////////////////////// // accessors /////////////////////////////////////////////////////////////////////// this.loadScene = function() { var jObj = this._jObj; if (!jObj.children) // || (jObj.children.length != 1)) throw new Error( "ill-formed JSON for runtime load: " + jObj ); var nChildren = jObj.children.length; // parse the data var child; if (jObj.scenedata) { this._useWebGL = true; var rdgeStr = jObj.scenedata; this.myScene.importJSON( rdgeStr ); for (var i=0; i<nChildren; i++) { child = jObj.children[i]; this.importObjects( child ); } //this.linkMaterials( this._geomRoot ); this.linkMaterials( this._rootChildren ); this.initMaterials(); this.linkLights(); } else { this._context = this._canvas.getContext( "2d" ); for (var i=0; i<nChildren; i++) { child = jObj.children[i]; this.importObjects( child ); } this.render(); } }; this.init = function() { var ctx1 = RDGE.globals.engine.ctxMan.handleToObject(this._canvas.rdgeCtxHandle), ctx2 = RDGE.globals.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 RDGE.camera(); this._camera = cam; cam.setPerspective(this.getFOV(), this.getAspect(), this.getZNear(), this.getZFar()); cam.setLookAt([0, 0, this.getViewDistance()], [0, 0, 0], RDGE.vec3.up()); // make this camera the active camera this.renderer.cameraManager().setActiveCamera(cam); // change clear color this.renderer.setClearColor([1.0, 1.0, 1.0, 0.0]); // create an empty scene graph this.myScene = new RDGE.SceneGraph(); // load the scene graph data this.loadScene(); // Add the scene to the engine - necessary if you want the engine to draw for you var name = "myScene" + this._canvas.getAttribute( "data-RDGE-id" ); RDGE.globals.engine.AddScene(name, this.myScene); this._initialized = true; }; // main code for handling user interaction and updating the scene this.update = function(dt) { if (this._initialized) { if (!dt) dt = 0.2; dt = 0.01; // use our own internal throttle this.elapsed += dt; // changed the global position uniform of light 0, another way to change behavior of a light RDGE.rdgeGlobalParameters.u_light0Pos.set( [5*Math.cos(this.elapsed), 5*Math.sin(this.elapsed), 20]); // orbit the light nodes around the boxes if (this.light ) 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)]); if (this.light2) 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(); // now update all the nodes in the scene this.myScene.update(dt); } }; this.updateMaterials = function() { var nMats = this._materials.length; for (var i=0; i<nMats; i++) { var mat = this._materials[i]; mat.update(); } }; // defining the draw function to control how the scene is rendered this.draw = function() { if (this._initialized) { RDGE.globals.engine.setContext( this._canvas.rdgeid ); var ctx = RDGE.globals.engine.getContext(); var renderer = ctx.renderer; if (renderer.unloadedTextureCount <= 0) { renderer.disableCulling(); renderer._clear(); this.myScene.render(); if (this._firstRender) { if (this._canvas.task) { this._firstRender = false; //this._canvas.task.stop(); } } } } }; this.importObjects = function( jObj, parent ) { // read the next object var gObj = this.importObject( jObj, parent ); // load the children if (jObj.children) { var nKids = jObj.children.length; for (var i=0; i<nKids; i++) { var child = jObj.children[i]; this.importObjects( child, gObj ); } } }; this.importObject = function( jObj, parent ) { var type = jObj.type; var obj; switch (type) { case 1: obj = new NinjaCvsRt.RuntimeRectangle(); obj.importJSON( jObj ); break; case 2: // circle obj = new NinjaCvsRt.RuntimeOval(); obj.importJSON( jObj ); break; case 3: // line obj = new NinjaCvsRt.RuntimeLine(); obj.importJSON( jObj ); break; default: throw new Error( "Attempting to load unrecognized object type: " + type ); break; } if (obj) this.addObject( obj, parent ); return obj; }; this.addObject = function( obj, parent ) { if (!obj) return; obj.setWorld( this ); if (parent == null) { //this._geomRoot = obj; this._rootChildren.push( obj ); } else parent.addChild( obj ); }; this.linkLights = function() { var matNode = this.findMaterialNode( "lights", this.myScene.scene ); if (matNode) { this.light = matNode.lightChannel[1]; this.light2 = matNode.lightChannel[2]; } }; this.linkMaterials = function( objArray ) { if (!objArray) return; for (var i=0; i<objArray.length; i++) { var obj = objArray[i]; // get the array of materials from the object var matArray = obj._materials; var nMats = matArray.length; for (var j=0; j<nMats; j++) { var mat = matArray[j]; var nodeName = mat._materialNodeName; var matNode = this.findMaterialNode( nodeName, this.myScene.scene ); if (matNode) { mat._materialNode = matNode; mat._shader = matNode.shaderProgram; this._materials.push( mat ); } } this.linkMaterials( obj.children ); } }; this.initMaterials = function() { var nMats = this._materials.length; for (var i=0; i<nMats; i++) { var mat = this._materials[i]; mat.init( this ); } }; this.findMaterialNode = function( nodeName, node ) { if (node.transformNode) node = node.transformNode; if (node.materialNode) { if (nodeName === node.materialNode.name) return node.materialNode; } if (node.children) { var nKids = node.children.length; for (var i=0; i<nKids; i++) { var child = node.children[i]; var rtnNode = this.findMaterialNode( nodeName, child ); if (rtnNode) return rtnNode; } } }; this.render = function( obj ) { //if (!obj) obj = this._geomRoot; //obj.render(); var children; if (obj) { obj.render(); children = obj.children; } else children = this._rootChildren; if (children) { var nKids = children.length; for (var i=0; i<nKids; i++) { var child = children[i]; if (child) this.render( child ); } } }; // start RDGE or load Canvas 2D objects if (jObj.scenedata) this._useWebGL = true; if (this._useWebGL) { var id = canvas.getAttribute( "data-RDGE-id" ); canvas.rdgeid = id; RDGE.globals.engine.registerCanvas(canvas, this); RDGE.RDGEStart( canvas ); } else { this.loadScene(); } }; /////////////////////////////////////////////////////////////////////// // Class RuntimeGeomObj // Super class for all geometry classes /////////////////////////////////////////////////////////////////////// NinjaCvsRt.RuntimeGeomObj = function () { /////////////////////////////////////////////////////////////////////// // Constants /////////////////////////////////////////////////////////////////////// this.GEOM_TYPE_RECTANGLE = 1; this.GEOM_TYPE_CIRCLE = 2; this.GEOM_TYPE_LINE = 3; this.GEOM_TYPE_PATH = 4; this.GEOM_TYPE_CUBIC_BEZIER = 5; this.GEOM_TYPE_UNDEFINED = -1; /////////////////////////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////////////////////////// this._children; // stroke and fill colors this._strokeColor = [0,0,0,0]; this._fillColor = [0,0,0,0]; // array of materials this._materials = []; /////////////////////////////////////////////////////////////////////// // Property accessors /////////////////////////////////////////////////////////////////////// this.geomType = function() { return this.GEOM_TYPE_UNDEFINED; }; this.setWorld = function(w) { this._world = w; }; this.getWorld = function() { return this._world; }; /////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////// this.addChild = function( child ) { if (!this._children) this._children = []; this._children.push( child ); }; this.importJSON = function() { }; this.importMaterials = function(jObj) { if (!jObj || !jObj.materials) return; var nMaterials = jObj.nMaterials; var matArray = jObj.materials; for (var i=0; i<nMaterials; i++) { var mat; var matObj = matArray[i].material; var matNodeName = matArray[i].materialNodeName; var shaderName = matObj.material; switch (shaderName) { case "flat": mat = new NinjaCvsRt.RuntimeFlatMaterial(); break; case "radialGradient": mat = new NinjaCvsRt.RuntimeRadialGradientMaterial(); break; case "linearGradient": mat = new NinjaCvsRt.RuntimeLinearGradientMaterial(); break; case "bumpMetal": mat = new NinjaCvsRt.RuntimeBumpMetalMaterial(); break; case "uber": mat = new NinjaCvsRt.RuntimeUberMaterial(); break; case "plasma": mat = new NinjaCvsRt.RuntimePlasmaMaterial(); break; case "deform": case "water": case "paris": case "raiders": case "tunnel": case "reliefTunnel": case "squareTunnel": case "twist": case "fly": case "julia": case "mandel": case "star": case "zinvert": case "keleidoscope": case "radialBlur": case "pulse": mat = new NinjaCvsRt.RuntimePulseMaterial(); break; default: console.log( "material type: " + shaderName + " is not supported" ); break; } if (mat) { mat.importJSON( matObj ); mat._materialNodeName = matNodeName; this._materials.push( mat ); } } }; //////////////////////////////////////////////////////////////////// // vector function this.vecAdd = function( dimen, a, b ) { var rtnVec; if ((a.length < dimen) || (b.length < dimen)) { throw new Error( "dimension error in vecAdd" ); } rtnVec = []; for (var i=0; i<dimen; i++) rtnVec[i] = a[i] + b[i]; return rtnVec; }; this.vecSubtract = function( dimen, a, b ) { var rtnVec; if ((a.length < dimen) || (b.length < dimen)) { throw new Error( "dimension error in vecSubtract" ); } rtnVec = []; for (var i=0; i<dimen; i++) rtnVec[i] = a[i] - b[i]; return rtnVec; }; this.vecDot = function( dimen, v0, v1 ) { if ((v0.length < dimen) || (v1.length < dimen)) { throw new Error( "dimension error in vecDot" ); } var sum = 0.0; for (var i=0; i<dimen; i++) sum += v0[i] * v1[i]; return sum; }; this.vecMag = function( dimen, vec ) { var sum = 0.0; for (var i=0; i<dimen; i++) sum += vec[i]*vec[i]; return Math.sqrt( sum ); }; this.vecScale = function(dimen, vec, scale) { for (var i=0; i<dimen; i++) vec[i] *= scale; return vec; }; this.vecNormalize = function(dimen, vec, len) { var rtnVec; if (!len) len = 1.0; var sum = 0.0, i = 0; for (i = 0; i<dimen; i++) sum += vec[i]*vec[i]; sum = Math.sqrt( sum ); if (Math.abs(sum) >= 0.001) { var scale = len/sum; rtnVec = []; for (i = 0; i<dimen; i++) rtnVec[i] = vec[i]*scale; } return rtnVec; }; this.transformPoint = function( srcPt, mat ) { var pt = srcPt.slice(0); var x = this.vecDot(3, pt, [mat[0], mat[4], mat[ 8]] ) + mat[12], y = this.vecDot(3, pt, [mat[1], mat[5], mat[ 9]] ) + mat[13], z = this.vecDot(3, pt, [mat[2], mat[6], mat[10]] ) + mat[14]; return [x, y, z]; }; this.MatrixIdentity = function(dimen) { var mat = []; for (var i = 0; i<dimen*dimen; i++) { mat.push(0); } var index = 0; for (var j = 0; j<dimen; j++) { mat[index] = 1.0; index += dimen + 1; } return mat; }; this.MatrixRotationZ = function( angle ) { var mat = this.MatrixIdentity(4); //glmat4.rotateZ(mat, angle); var sn = Math.sin(angle), cs = Math.cos(angle); mat[0] = cs; mat[4] = -sn; mat[1] = sn; mat[5] = cs; return mat; }; }; /////////////////////////////////////////////////////////////////////// // Class RuntimeRectangle /////////////////////////////////////////////////////////////////////// NinjaCvsRt.RuntimeRectangle = function () { // inherit the members of NinjaCvsRt.RuntimeGeomObj this.inheritedFrom = NinjaCvsRt.RuntimeGeomObj; this.inheritedFrom(); this.importJSON = function( jObj ) { this._xOffset = jObj.xoff; this._yOffset = jObj.yoff; this._width = jObj.width; this._height = jObj.height; this._strokeWidth = jObj.strokeWidth; this._strokeColor = jObj.strokeColor; this._fillColor = jObj.fillColor; this._tlRadius = jObj.tlRadius; this._trRadius = jObj.trRadius; this._blRadius = jObj.blRadius; this._brRadius = jObj.brRadius; this._innerRadius = jObj.innerRadius; this._strokeStyle = jObj.strokeStyle; var strokeMaterialName = jObj.strokeMat; var fillMaterialName = jObj.fillMat; this.importMaterials( jObj.materials ); }; this.renderPath = function( inset, ctx ) { // various declarations var pt, rad, ctr, startPt, bPts; var width = Math.round(this._width), height = Math.round(this._height), hw = 0.5*width, hh = 0.5*height; pt = [inset, inset]; // top left corner var tlRad = this._tlRadius; //top-left radius var trRad = this._trRadius; var blRad = this._blRadius; var brRad = this._brRadius; // limit the radii to half the rectangle dimension var minDimen = hw < hh ? hw : hh; if (tlRad > minDimen) tlRad = minDimen; if (blRad > minDimen) blRad = minDimen; if (brRad > minDimen) brRad = minDimen; if (trRad > minDimen) trRad = minDimen; var world = this.getWorld(); var vpw = world.getViewportWidth(), vph = world.getViewportHeight(); var cop = [0.5*vpw, 0.5*vph, 0.0]; var xCtr = cop[0] + this._xOffset, yCtr = cop[1] - this._yOffset; var xLeft = xCtr - 0.5*this._width, yTop = yCtr - 0.5*this._height; var xDist = cop[0] - xLeft, yDist = cop[1] - yTop; var xOff = 0.5*vpw - xDist, yOff = 0.5*vph - yDist; if ((tlRad <= 0) && (blRad <= 0) && (brRad <= 0) && (trRad <= 0)) { ctx.rect(pt[0]+xOff, pt[1]+yOff, width - 2*inset, height - 2*inset); } else { // get the top left point rad = tlRad - inset; if (rad < 0) rad = 0; pt[1] += rad; if (Math.abs(rad) < 0.001) pt[1] = inset; ctx.moveTo( pt[0]+xOff, pt[1]+yOff ); // get the bottom left point pt = [inset, height - inset]; rad = blRad - inset; if (rad < 0) rad = 0; pt[1] -= rad; ctx.lineTo( pt[0]+xOff, pt[1]+yOff ); // get the bottom left curve if (rad > 0.001) ctx.quadraticCurveTo( inset+xOff, height-inset+yOff, inset+rad+xOff, height-inset+yOff ); // do the bottom of the rectangle pt = [width - inset, height - inset]; rad = brRad - inset; if (rad < 0) rad = 0; pt[0] -= rad; ctx.lineTo( pt[0]+xOff, pt[1]+yOff ); // get the bottom right arc if (rad > 0.001) ctx.quadraticCurveTo( width-inset+xOff, height-inset+yOff, width-inset+xOff, height-inset-rad+yOff ); // get the right of the rectangle pt = [width - inset, inset]; rad = trRad - inset; if (rad < 0) rad = 0; pt[1] += rad; ctx.lineTo( pt[0]+xOff, pt[1]+yOff ); // do the top right corner if (rad > 0.001) ctx.quadraticCurveTo( width-inset+xOff, inset+yOff, width-inset-rad+xOff, inset+yOff ); // do the top of the rectangle pt = [inset, inset]; rad = tlRad - inset; if (rad < 0) rad = 0; pt[0] += rad; ctx.lineTo( pt[0]+xOff, pt[1]+yOff); // do the top left corner if (rad > 0.001) ctx.quadraticCurveTo( inset+xOff, inset+yOff, inset+xOff, inset+rad+yOff ); else ctx.lineTo( inset+xOff, 2*inset+yOff ); } }; this.render = function() { // get the world var world = this.getWorld(); if (!world) throw( "null world in rectangle render" ); // get the context var ctx = world.get2DContext(); if (!ctx) return; // get some dimensions var lw = this._strokeWidth; var w = world.getViewportWidth(), h = world.getViewportHeight(); var c, inset, gradient, colors, len, n, position, cs; // render the fill ctx.beginPath(); if (this._fillColor) { inset = Math.ceil( lw ) - 0.5; if(this._fillColor.gradientMode) { if(this._fillColor.gradientMode === "radial") { gradient = ctx.createRadialGradient(w/2, h/2, 0, w/2, h/2, Math.max(w, h)/2); } else { gradient = ctx.createLinearGradient(inset/2, h/2, w-inset, h/2); } colors = this._fillColor.color; len = colors.length; for(n=0; n<len; n++) { position = colors[n].position/100; cs = colors[n].value; gradient.addColorStop(position, "rgba(" + cs.r + "," + cs.g + "," + cs.b + "," + cs.a + ")"); } ctx.fillStyle = gradient; } else { c = "rgba(" + 255*this._fillColor[0] + "," + 255*this._fillColor[1] + "," + 255*this._fillColor[2] + "," + this._fillColor[3] + ")"; ctx.fillStyle = c; } ctx.lineWidth = lw; this.renderPath( inset, ctx ); ctx.fill(); ctx.closePath(); } // render the stroke ctx.beginPath(); if (this._strokeColor) { inset = Math.ceil( 0.5*lw ) - 0.5; if(this._strokeColor.gradientMode) { if(this._strokeColor.gradientMode === "radial") { gradient = ctx.createRadialGradient(w/2, h/2, Math.min(h, w)/2-inset, w/2, h/2, Math.max(h, w)/2); } else { gradient = ctx.createLinearGradient(0, h/2, w, h/2); } colors = this._strokeColor.color; len = colors.length; for(n=0; n<len; n++) { position = colors[n].position/100; cs = colors[n].value; gradient.addColorStop(position, "rgba(" + cs.r + "," + cs.g + "," + cs.b + "," + cs.a + ")"); } ctx.strokeStyle = gradient; } else { c = "rgba(" + 255*this._strokeColor[0] + "," + 255*this._strokeColor[1] + "," + 255*this._strokeColor[2] + "," + this._strokeColor[3] + ")"; ctx.strokeStyle = c; } ctx.lineWidth = lw; this.renderPath( inset, ctx ); ctx.stroke(); ctx.closePath(); } }; }; /////////////////////////////////////////////////////////////////////// // Class RuntimeLine /////////////////////////////////////////////////////////////////////// NinjaCvsRt.RuntimeLine = function () { this.inheritedFrom = NinjaCvsRt.RuntimeGeomObj; this.inheritedFrom(); this.importJSON = function( jObj ) { this._xOffset = jObj.xoff; this._yOffset = jObj.yoff; this._width = jObj.width; this._height = jObj.height; this._xAdj = jObj.xAdj; this._yAdj = jObj.yAdj; this._strokeWidth = jObj.strokeWidth; this._slope = jObj.slope; this._strokeStyle = jObj.strokeStyle; this._strokeColor = jObj.strokeColor; var strokeMaterialName = jObj.strokeMat; this.importMaterials( jObj.materials ); }; this.render = function() { // get the world var world = this.getWorld(); if (!world) throw( "null world in buildBuffers" ); // get the context var ctx = world.get2DContext(); if (!ctx) return; // set up the stroke style var lineWidth = this._strokeWidth, w = this._width, h = this._height; var c, gradient, colors, len, n, position, cs; ctx.beginPath(); ctx.lineWidth = lineWidth; if (this._strokeColor) { if(this._strokeColor.gradientMode) { if(this._strokeColor.gradientMode === "radial") { gradient = ctx.createRadialGradient(w/2, h/2, 0, w/2, h/2, Math.max(w/2, h/2)); } else { gradient = ctx.createLinearGradient(0, h/2, w, h/2); } colors = this._strokeColor.color; len = colors.length; for(n=0; n<len; n++) { position = colors[n].position/100; cs = colors[n].value; gradient.addColorStop(position, "rgba(" + cs.r + "," + cs.g + "," + cs.b + "," + cs.a + ")"); } ctx.strokeStyle = gradient; } else { c = "rgba(" + 255*this._strokeColor[0] + "," + 255*this._strokeColor[1] + "," + 255*this._strokeColor[2] + "," + this._strokeColor[3] + ")"; ctx.strokeStyle = c; } // get the points var p0, p1; if(this._slope === "vertical") { p0 = [0.5*w, 0]; p1 = [0.5*w, h]; } else if(this._slope === "horizontal") { p0 = [0, 0.5*h]; p1 = [w, 0.5*h]; } else if(this._slope > 0) { p0 = [this._xAdj, this._yAdj]; p1 = [w - this._xAdj, h - this._yAdj]; } else { p0 = [this._xAdj, h - this._yAdj]; p1 = [w - this._xAdj, this._yAdj]; } // draw the line ctx.moveTo( p0[0], p0[1] ); ctx.lineTo( p1[0], p1[1] ); ctx.stroke(); } }; }; /////////////////////////////////////////////////////////////////////// // Class RuntimeOval /////////////////////////////////////////////////////////////////////// NinjaCvsRt.RuntimeOval = function () { // inherit the members of NinjaCvsRt.RuntimeGeomObj this.inheritedFrom = NinjaCvsRt.RuntimeGeomObj; this.inheritedFrom(); this.importJSON = function( jObj ) { this._xOffset = jObj.xoff; this._yOffset = jObj.yoff; this._width = jObj.width; this._height = jObj.height; this._strokeWidth = jObj.strokeWidth; this._strokeColor = jObj.strokeColor; this._fillColor = jObj.fillColor; this._innerRadius = jObj.innerRadius; this._strokeStyle = jObj.strokeStyle; var strokeMaterialName = jObj.strokeMat; var fillMaterialName = jObj.fillMat; this.importMaterials( jObj.materials ); }; this.render = function() { // get the world var world = this.getWorld(); if (!world) throw( "null world in buildBuffers" ); // get the context var ctx = world.get2DContext(); if (!ctx) return; // declare some variables var p0, p1; var x0, y0, x1, y1; // create the matrix var lineWidth = this._strokeWidth; var innerRad = this._innerRadius; var xScale = 0.5*this._width - lineWidth, yScale = 0.5*this._height - lineWidth; // translate var xCtr = 0.5*world.getViewportWidth() + this._xOffset, yCtr = 0.5*world.getViewportHeight() + this._yOffset; var mat = this.MatrixIdentity( 4 ); mat[0] = xScale; mat[12] = xCtr; mat[5] = yScale; mat[13] = yCtr; /* var mat = [ [ xScale, 0.0, 0.0, xCtr], [ 0.0, yScale, 0.0, yCtr], [ 0.0, 0.0, 1.0, 0.0], [ 0.0, 0.0, 0.0, 1.0] ]; */ // get a bezier representation of the circle var bezPts = this.circularArcToBezier( [0,0,0], [1,0,0], 2.0*Math.PI ); if (bezPts) { var n = bezPts.length; var gradient, colors, len, j, position, cs, c; // set up the fill style ctx.beginPath(); ctx.lineWidth = 0; if (this._fillColor) { if(this._fillColor.gradientMode) { if(this._fillColor.gradientMode === "radial") { gradient = ctx.createRadialGradient(xCtr, yCtr, 0, xCtr, yCtr, Math.max(this._width, this._height)/2); } else { gradient = ctx.createLinearGradient(lineWidth/2, this._height/2, this._width-lineWidth, this._height/2); } colors = this._fillColor.color; len = colors.length; for(j=0; j<len; j++) { position = colors[j].position/100; cs = colors[j].value; gradient.addColorStop(position, "rgba(" + cs.r + "," + cs.g + "," + cs.b + "," + cs.a + ")"); } ctx.fillStyle = gradient; } else { c = "rgba(" + 255*this._fillColor[0] + "," + 255*this._fillColor[1] + "," + 255*this._fillColor[2] + "," + this._fillColor[3] + ")"; ctx.fillStyle = c; } // draw the fill // ctx.beginPath(); var p = this.transformPoint( bezPts[0], mat ); ctx.moveTo( p[0], p[1] ); var index = 1; while (index < n) { p0 = this.transformPoint( bezPts[index], mat ); p1 = this.transformPoint( bezPts[index+1], mat ); x0 = p0[0]; y0 = p0[1]; x1 = p1[0]; y1 = p1[1]; ctx.quadraticCurveTo( x0, y0, x1, y1 ); index += 2; } if (innerRad > 0.001) { xScale = 0.5*innerRad*this._width; yScale = 0.5*innerRad*this._height; mat[0] = xScale; mat[5] = yScale; // get the bezier points var bezPtsInside = this.circularArcToBezier( [0,0,0], [1,0,0], -2.0*Math.PI ); if (bezPtsInside) { n = bezPtsInside.length; p = this.transformPoint( bezPtsInside[0], mat ); ctx.moveTo( p[0], p[1] ); index = 1; while (index < n) { p0 = this.transformPoint( bezPtsInside[index], mat ); p1 = this.transformPoint( bezPtsInside[index+1], mat ); x0 = p0[0]; y0 = p0[1]; x1 = p1[0]; y1 = p1[1]; ctx.quadraticCurveTo( x0, y0, x1, y1 ); index += 2; } } } // fill the path ctx.fill(); } // calculate the stroke matrix xScale = 0.5*this._width - 0.5*lineWidth; yScale = 0.5*this._height - 0.5*lineWidth; mat[0] = xScale; mat[5] = yScale; // set up the stroke style ctx.beginPath(); ctx.lineWidth = lineWidth; if (this._strokeColor) { if(this._strokeColor.gradientMode) { if(this._strokeColor.gradientMode === "radial") { gradient = ctx.createRadialGradient(xCtr, yCtr, Math.min(xScale, yScale), xCtr, yCtr, 0.5*Math.max(this._height, this._width)); } else { gradient = ctx.createLinearGradient(0, this._height/2, this._width, this._height/2); } colors = this._strokeColor.color; len = colors.length; for(j=0; j<len; j++) { position = colors[j].position/100; cs = colors[j].value; gradient.addColorStop(position, "rgba(" + cs.r + "," + cs.g + "," + cs.b + "," + cs.a + ")"); } ctx.strokeStyle = gradient; } else { c = "rgba(" + 255*this._strokeColor[0] + "," + 255*this._strokeColor[1] + "," + 255*this._strokeColor[2] + "," + this._strokeColor[3] + ")"; ctx.strokeStyle = c; } // draw the stroke p = this.transformPoint( bezPts[0], mat ); ctx.moveTo( p[0], p[1] ); index = 1; while (index < n) { p0 = this.transformPoint( bezPts[index], mat ); p1 = this.transformPoint( bezPts[index+1], mat ); x0 = p0[0]; y0 = p0[1]; x1 = p1[0]; y1 = p1[1]; ctx.quadraticCurveTo( x0, y0, x1, y1 ); index += 2; } if (innerRad > 0.001) { // calculate the stroke matrix xScale = 0.5*innerRad*this._width - 0.5*lineWidth; yScale = 0.5*innerRad*this._height - 0.5*lineWidth; mat[0] = xScale; mat[5] = yScale; // draw the stroke p = this.transformPoint( bezPts[0], mat ); ctx.moveTo( p[0], p[1] ); index = 1; while (index < n) { p0 = this.transformPoint( bezPts[index], mat ); p1 = this.transformPoint( bezPts[index+1], mat ); x0 = p0[0]; y0 = p0[1]; x1 = p1[0]; y1 = p1[1]; ctx.quadraticCurveTo( x0, y0, x1, y1 ); index += 2; } } // render the stroke ctx.stroke(); } } }; /////////////////////////////////////////////////////////////////////// // this function returns the quadratic Bezier approximation to the specified // circular arc. The input can be 2D or 3D, determined by the minimum dimension // of the center and start point. // includedAngle is in radians, can be positiveor negative this.circularArcToBezier= function( ctr_, startPt_, includedAngle ) { var dimen = 3; var ctr = ctr_.slice(); var startPt = startPt_.slice(); // make sure the start point is good var pt = this.vecSubtract(dimen, startPt, ctr); var rad = this.vecMag(dimen, pt); if ((dimen != 3) || (rad <= 0) || (includedAngle === 0)) { if (dimen != 3) console.log( "circularArcToBezier works for 3 dimensional points only. Was " + dimen ); return [ startPt.slice(0), startPt.slice(0), startPt.slice(0) ]; } // determine the number of segments. 45 degree span maximum. var nSegs = Math.ceil( Math.abs(includedAngle)/(0.25*Math.PI) ); if (nSegs <= 0) return [ startPt.slice(0), startPt.slice(0), startPt.slice(0) ]; var dAngle = includedAngle/nSegs; // determine the length of the center control point from the circle center var cs = Math.cos( 0.5*Math.abs(dAngle) ), sn = Math.sin( 0.5*Math.abs(dAngle) ); var c = rad*sn; var h = c*sn/cs; var d = rad*cs + h; var rtnPts = [ this.vecAdd(dimen, pt, ctr) ]; var rotMat = this.MatrixRotationZ( dAngle ); for ( var i=0; i<nSegs; i++) { // get the next end point var pt2 = this.transformPoint( pt, rotMat ); // get the next center control point var midPt = this.vecAdd(3, pt, pt2); this.vecScale(dimen, midPt, 0.5); midPt = this.vecNormalize( dimen, midPt, d ); // save the next segment rtnPts.push( this.vecAdd(dimen, midPt, ctr) ); rtnPts.push( this.vecAdd(dimen, pt2, ctr) ); // advance for the next segment pt = pt2; } return rtnPts; }; }; /////////////////////////////////////////////////////////////////////// // Class RuntimeMaterial // Runtime representation of a material. /////////////////////////////////////////////////////////////////////// NinjaCvsRt.RuntimeMaterial = function ( world ) { /////////////////////////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////////////////////////// this._name = "GLMaterial"; this._shaderName = "undefined"; // variables for animation speed this._time = 0.0; this._dTime = 0.01; // RDGE variables this._shader; this._materialNode; /////////////////////////////////////////////////////////////////////// // Property Accessors /////////////////////////////////////////////////////////////////////// // a material can be animated or not. default is not. // Any material needing continuous rendering should override this method this.isAnimated = function() { return false; }; /////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////// this.init = function( world ) { }; this.update = function( time ) { }; this.importJSON = function( jObj ) { }; }; NinjaCvsRt.RuntimeFlatMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeMaterial; this.inheritedFrom(); this._name = "FlatMaterial"; this._shaderName = "flat"; // assign a default color this._color = [1,0,0,1]; this.importJSON = function( jObj ) { this._color = jObj.color; }; this.init = function( world ) { if (this._shader) { this._shader.colorMe["color"].set( this._color ); } }; }; NinjaCvsRt.RuntimePulseMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeMaterial; this.inheritedFrom(); this._name = "PulseMaterial"; this._shaderName = "pulse"; this._texMap = 'assets/images/cubelight.png'; this.isAnimated = function() { return true; }; this.importJSON = function( jObj ) { this._texMap = jObj.texture; if (jObj.dTime) this._dTime = jObj.dTime; }; this.init = function( world ) { var material = this._materialNode; if (material) { var technique = material.shaderProgram["default"]; var renderer = RDGE.globals.engine.getContext().renderer; if (renderer && technique) { if (this._shader && this._shader["default"]) { var res = [ renderer.vpWidth, renderer.vpHeight ]; technique.u_resolution.set( res ); var wrap = 'REPEAT', mips = true; var tex = renderer.getTextureByName(this._texMap, wrap, mips ); if (tex) technique.u_tex0.set( tex ); this._shader["default"].u_time.set( [this._time] ); } } } }; // several materials inherit from pulse. // they may share this update method this.update = function( time ) { var material = this._materialNode; if (material) { var technique = material.shaderProgram["default"]; var renderer = RDGE.globals.engine.getContext().renderer; if (renderer && technique) { if (this._shader && this._shader["default"]) this._shader["default"].u_time.set( [this._time] ); this._time += this._dTime; if (this._time > 200.0) this._time = 0.0; } } }; }; NinjaCvsRt.RuntimeRadialGradientMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeMaterial; this.inheritedFrom(); this._name = "RadialGradientMaterial"; this._shaderName = "radialGradient"; // setup default values this._color1 = [1,0,0,1]; this._colorStop1 = 0.0; this._color2 = [0,1,0,1]; this._colorStop2 = 0.3; this._color3 = [0,1,0,1]; this._colorStop3 = 0.6; this._color4 = [0,1,0,1]; this._colorStop4 = 1.0; this.init = function() { var material = this._materialNode; if (material) { var technique = material.shaderProgram["default"]; var renderer = RDGE.globals.engine.getContext().renderer; if (renderer && technique) { if (this._shader && this._shader["default"]) { this._shader["default"].u_color1.set( this._color1 ); this._shader["default"].u_color2.set( this._color2 ); this._shader["default"].u_color3.set( this._color3 ); this._shader["default"].u_color4.set( this._color4 ); this._shader["default"].u_colorStop1.set( [this._colorStop1] ); this._shader["default"].u_colorStop2.set( [this._colorStop2] ); this._shader["default"].u_colorStop3.set( [this._colorStop3] ); this._shader["default"].u_colorStop4.set( [this._colorStop4] ); if (this._angle !== undefined) this._shader["default"].u_cos_sin_angle.set([Math.cos(this._angle), Math.sin(this._angle)]); } } } }; this.importJSON = function( jObj ) { this._color1 = jObj.color1; this._color2 = jObj.color2; this._color3 = jObj.color3; this._color4 = jObj.color4; this._colorStop1 = jObj.colorStop1; this._colorStop2 = jObj.colorStop2; this._colorStop3 = jObj.colorStop3; this._colorStop4 = jObj.colorStop4; if (this._angle !== undefined) this._angle = jObj.angle; }; }; NinjaCvsRt.RuntimeLinearGradientMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeRadialGradientMaterial; this.inheritedFrom(); this._name = "LinearGradientMaterial"; this._shaderName = "linearGradient"; // the only difference between linear & radial gradient is the existance of an angle for linear. this._angle = 0.0; }; NinjaCvsRt.RuntimeBumpMetalMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeMaterial; this.inheritedFrom(); this._name = "BumpMetalMaterial"; this._shaderName = "bumpMetal"; this._lightDiff = [0.3, 0.3, 0.3, 1.0]; this._diffuseTexture = "assets/images/metal.png"; this._specularTexture = "assets/images/silver.png"; this._normalTexture = "assets/images/normalMap.png"; this.importJSON = function( jObj ) { this._lightDiff = jObj.lightDiff; this._diffuseTexture = jObj.diffuseTexture; this._specularTexture = jObj.specularTexture; this._normalTexture = jObj.normalMap; }; this.init = function( world ) { var material = this._materialNode; if (material) { var technique = material.shaderProgram["default"]; var renderer = RDGE.globals.engine.getContext().renderer; if (renderer && technique) { if (this._shader && this._shader["default"]) { technique.u_light0Diff.set( this._lightDiff ); var tex; var wrap = 'REPEAT', mips = true; if (this._diffuseTexture) { tex = renderer.getTextureByName(this._diffuseTexture, wrap, mips ); if (tex) technique.u_colMap.set( tex ); } if (this._normalTexture) { tex = renderer.getTextureByName(this._normalTexture, wrap, mips ); if (tex) technique.u_normalMap.set( tex ); } if (this._specularTexture) { tex = renderer.getTextureByName(this._specularTexture, wrap, mips ); technique.u_glowMap.set( tex ); } } } } }; }; NinjaCvsRt.RuntimeUberMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeMaterial; this.inheritedFrom(); this._MAX_LIGHTS = 4; this.init = function( ) { var material = this._materialNode; if (material) { var technique = material.shaderProgram.defaultTechnique; var renderer = RDGE.globals.engine.getContext().renderer; if (renderer && technique) { if (this._shader && this._shader.defaultTechnique) { if (this._ambientColor && technique.u_ambientColor) technique.u_ambientColor.set(this._ambientColor ); if (this._diffuseColor && technique.u_diffuseColor ) technique.u_diffuseColor.set(this._diffuseColor ); if (this._specularColor && technique.u_specularColor) technique.u_specularColor.set(this._specularColor); if (this._specularPower && technique.u_specularPower) technique.u_specularPower.set([this._specularPower]); if (this._lights) { for(var i = 0; i < 4; ++i) { var light = this._lights[i]; if (light) { if(light.type == 'directional') { technique['u_light'+i+'Dir'].set( light.direction || [ 0, 0, 1 ]); } else if(light.type == 'spot') { var deg2Rad = Math.PI / 180; technique['u_light'+i+'Atten'].set(light.attenuation || [ 1,0,0 ]); technique['u_light'+i+'Pos'].set(light.position || [ 0, 0, 0 ]); technique['u_light'+i+'Spot'].set([ Math.cos( ( light.spotInnerCutoff || 45.0 ) * deg2Rad ), Math.cos( ( light.spotOuterCutoff || 90.0 ) * deg2Rad )]); } else { technique['u_light'+i+'Pos'].set(light.position || [ 0, 0, 0 ]); technique['u_light'+i+'Atten'].set(light.attenuation || [ 1,0,0 ]); } // set the common light properties technique['u_light'+i+'Color'].set(light.diffuseColor || [ 1,1,1,1 ]); technique['u_light'+i+'Specular'].set(light.specularColor || [ 1, 1, 1, 1 ]); } } } // currently not exported var uvTransform = [ 2.0, 0, 0, 0, 0, 2.0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1]; technique.u_uvMatrix.set(uvTransform); var tex = null; if (this._diffuseMap) { tex = renderer.getTextureByName(this._diffuseMap, 'REPEAT'); technique.s_diffuseMap.set( tex ); } if (this._normalMap) { tex = renderer.getTextureByName(this._normalMap, 'REPEAT'); technique.s_normalMap.set( tex ); } if (this._specularMap) { tex = renderer.getTextureByName(this._specularMap, 'REPEAT'); technique.s_specMap.set( tex ); } if(this._environmentMap) { tex = renderer.getTextureByName(this._environmentMap, 'CLAMP'); technique.s_envMap.set( tex ); if (this._environmentAmount) technique.u_envReflection.set([ this._environmentAmount ] ); } } } } }; this.update = function( time ) { }; this.importJSON = function( jObj ) { if (jObj.materialProps) { this._ambientColor = jObj.materialProps.ambientColor; this._diffuseColor = jObj.materialProps.diffuseColor; this._specularColor = jObj.materialProps.specularColor; this._specularPower = jObj.materialProps.specularPower; } var lightArray = jObj.lights; if (lightArray) { this._lights = []; for (var i=0; i<this._MAX_LIGHTS; i++) { var lightObj = lightArray[i]; if (lightObj) { var type = lightObj['light'+i]; if (type) { var light = new Object; switch (type) { case "directional": light.direction = lightObj['light' + i + 'Dir']; break; case "spot": light.position = lightObj['light' + i + 'Pos']; light['spotInnerCutoff'] = lightObj['light' + i + 'OuterSpotCutoff']; light['spotOuterCutoff'] = lightObj['light' + i + 'InnerSpotCutoff']; break; case "point": light.position = lightObj['light' + i + 'Pos']; light.attenuation = lightObj['light' + i + 'Attenuation']; break; default: throw new Error( "unrecognized light type on import: " + type ); break; } // common to all lights light.diffuseColor = lightObj['light' + i + 'Color']; light.specularColor = lightObj['light' + i + 'SpecularColor']; // push the light this._lights.push( light ); } else this._lights[i] = 'undefined'; } } } this._diffuseMap = jObj['diffuseMap']; this._normalMap = jObj['normalMap']; this._specularMap = jObj['specularMap']; this._environmentMap = jObj['environmentMap']; if (this._environmentMap) this._environmentAmount = jObj['environmentAmount']; }; }; NinjaCvsRt.RuntimePlasmaMaterial = function () { // inherit the members of NinjaCvsRt.RuntimeMaterial this.inheritedFrom = NinjaCvsRt.RuntimeMaterial; this.inheritedFrom(); this.init = function( ) { this.update(); }; this.importJSON = function( jObj ) { this._speed = jObj.speed; this._dTime = jObj.dTime; }; this.update = function( time ) { var material = this._materialNode; if (material) { var technique = material.shaderProgram["default"]; var renderer = RDGE.globals.engine.getContext().renderer; if (renderer && technique) { if (this._shader && this._shader["default"]) this._shader["default"].u_time.set( [this._time] ); this._time += this._dTime; if (this._time > 200.0) this._time = 0.0; } } }; };