/*
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.
*/
// namespace for the Ninja Canvas Runtime
var NinjaCvsRt = NinjaCvsRt || {};
///////////////////////////////////////////////////////////////////////
//Loading webGL/canvas data on window load
window.addEventListener('load', loadCanvasData, false);
//Load data function (on document loaded)
function loadCanvasData (e) {
//Cleaning up events
window.removeEventListener('load', loadCanvasData, false);
//Getting tag with data, MUST contain attribute
var xhr, tag = document.querySelectorAll(['script[data-ninja-canvas-lib]'])[0];
//Checking for data to be external file
if (tag.getAttribute('data-ninja-canvas-json') !== null) {
//Loading JSON data
xhr = new XMLHttpRequest();
xhr.open("GET", tag.getAttribute('data-ninja-canvas-json'), false);
xhr.send();
//Checking for data
if (xhr.readyState === 4) {
//Calling method to initialize all webGL/canvas(es)
NinjaCvsRt.initWebGl(document.body, tag.getAttribute('data-ninja-canvas-libpath'), xhr.response);
} else {
//TODO: Add error for users
}
} else {//Data in document itself
//Calling method to initialize all webGL/canvas(es)
NinjaCvsRt.initWebGl(document.body, tag.getAttribute('data-ninja-canvas-libpath'), document.querySelectorAll(['script[data-ninja-canvas]'])[0].innerHTML);
}
}
///////////////////////////////////////////////////////////////////////
//Loading webGL/canvas data
NinjaCvsRt.initWebGl = function (rootElement, directory, data) {
var cvsDataMngr, ninjaWebGlData = JSON.parse((data.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 = Object.create(NinjaCvsRt.CanvasDataManager, {});
//Loading data to canvas(es)
cvsDataMngr.loadGLData(rootElement, ninjaWebGlData.data, directory);
};
///////////////////////////////////////////////////////////////////////
// Class ShapeRuntime
// Manages runtime shape display
///////////////////////////////////////////////////////////////////////
NinjaCvsRt.CanvasDataManager = Object.create(Object.prototype, {
loadGLData: {
value: function(root, valueArray, assetPath) {
if (assetPath)
this._assetPath = assetPath.slice();
var value = valueArray;
var nWorlds = value.length;
for (var i=0; i= 0.001)
{
var scale = len/sum;
rtnVec = [];
for (i = 0; i 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 );
}
}
},
render: {
value: 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") {
var ww = w - 2*lw, hh = h - 2*lw;
gradient = ctx.createRadialGradient(w/2, h/2, 0, w/2, h/2, Math.max(ww, hh)/2);
} else {
gradient = ctx.createLinearGradient(inset, h/2, w-inset, h/2);
}
colors = this._fillColor.color;
len = colors.length;
for(n=0; n 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 = Object.create(NinjaCvsRt.RuntimeGeomObj, {
importJSON: {
value: 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 );
}
},
render: {
value: 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 - lineWidth);
} else {
gradient = ctx.createLinearGradient(lineWidth, this._height/2, this._width-lineWidth, this._height/2);
}
colors = this._fillColor.color;
len = colors.length;
for(j=0; j 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, 0,
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 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
circularArcToBezier: {
value: 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 this._twistAmount)
{
angle = this._twistAmount;
this._dTime = -this._dTime;
}
else if (angle < 0.0)
{
angle = 0;
this._dTime = -this._dTime;
}
this._angle = angle;
this._shader.twistMe["u_twistAmount"].set([angle]);
}
}
});
NinjaCvsRt.RuntimePulseMaterial = Object.create(NinjaCvsRt.RuntimeMaterial, {
_name: { value: "PulseMaterial", writable: true },
_shaderName: { value: "pulse", writable: true },
_texMap: { value: 'assets/images/cubelight.png', writable: true },
isAnimated: { value: function() { return true; }},
importJSON: {
value: function(jObj) {
this._texMap = jObj.texture;
if (jObj.dTime) this._dTime = jObj.dTime;
}
},
init: {
value: 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_speed.set( [1.0] );
this._shader["default"].u_time.set( [this._time] );
}
}
}
}
},
// several materials inherit from pulse.
// they may share this update method
update: {
value: 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.RuntimeFlagMaterial = Object.create(NinjaCvsRt.RuntimeMaterial, {
_name: { value: "FlagMaterial", writable: true },
_shaderName: { value: "flag", writable: true },
// default values
_texMap: { value: 'assets/images/cubelight.png', writable: true },
_speed: { value: 1.0, writable: true },
_waveWidth: { value: 1.0, writable: true },
_waveHeight: { value: 1.0, writable: true },
_dTime: { value: 0.1, writable: true },
importJSON: {
value: function(jObj) {
this._texMap = jObj.texMap;
this._waveWidth = jObj.waveWidth;
this._waveHeight = jObj.waveHeight;
this._speed = jObj.speed;
}
},
init: {
value: function(world) {
if (this._shader) {
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 wrap = 'REPEAT', mips = true;
var tex = renderer.getTextureByName(this._texMap, wrap, mips );
if (tex) technique.u_tex0.set( tex );
technique.u_speed.set( [this._speed] );
technique.u_waveWidth.set( [this._waveWidth] );
technique.u_waveHeight.set( [this._waveHeight] );
}
}
}
}
}
},
update: {
value: 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 * this._speed;
}
}
}
}
});
NinjaCvsRt.RuntimeRadialGradientMaterial = Object.create(NinjaCvsRt.RuntimeMaterial, {
_name: { value: "RadialGradientMaterial", writable: true },
_shaderName: { value: "radialGradient", writable: true },
// setup default values
_color1: { value: [1,0,0,1], writable: true },
_color2: { value: [0,1,0,1], writable: true },
_color3: { value: [0,0,1,1], writable: true },
_color4: { value: [0,1,1,1], writable: true },
_colorStop1: { value: 0.0, writable: true },
_colorStop2: { value: 0.3, writable: true },
_colorStop3: { value: 0.6, writable: true },
_colorStop4: { value: 1.0, writable: true },
_textureTransform: { value: [1,0,0, 0,1,0, 0,0,1], writable: true },
init: {
value: 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"])
{
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] );
this._shader["default"].u_texTransform.set( this._textureTransform );
if (this._angle !== undefined)
this._shader["default"].u_cos_sin_angle.set([Math.cos(this._angle), Math.sin(this._angle)]);
}
}
}
}
},
importJSON: {
value: 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;
this._textureTransform = jObj.textureTransform;
if (this._angle !== undefined)
this._angle = jObj.angle;
}
}
});
NinjaCvsRt.RuntimeLinearGradientMaterial = Object.create(NinjaCvsRt.RuntimeRadialGradientMaterial, {
_name: { value: "LinearGradientMaterial", writable: true },
_shaderName: { value: "linearGradient", writable: true },
// the only difference between linear & radial gradient is the existence of an angle for linear.
_angle: { value: 0.0, writable: true }
});
NinjaCvsRt.RuntimeBumpMetalMaterial = Object.create(NinjaCvsRt.RuntimeMaterial, {
_name: { value: "BumpMetalMaterial", writable: true },
_shaderName: { value: "bumpMetal", writable: true },
_lightDiff: { value: [0.3, 0.3, 0.3, 1.0], writable: true },
_diffuseTexture: { value: "assets/images/metal.png", writable: true },
_specularTexture: { value: "assets/images/silver.png", writable: true },
_normalTexture: { value: "assets/images/normalMap.png", writable: true },
importJSON: {
value: function(jObj) {
this._lightDiff = jObj.lightDiff;
this._diffuseTexture = jObj.diffuseTexture;
this._specularTexture = jObj.specularTexture;
this._normalTexture = jObj.normalMap;
}
},
init: {
value: 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 = Object.create(NinjaCvsRt.RuntimeMaterial, {
// _name: { value: "UberMaterial", writable: true },
// _shaderName: { value: "uber", writable: true },
_MAX_LIGHTS: { value: 4, writable: false },
init: {
value: function(world) {
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 ] );
}
}
}
}
}
},
update: {
value: function(time) {
}
},
importJSON: {
value: 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 200.0) this._time = 0.0;
}
}
}
}
});